effective-rsc 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LLMS.md +48 -7
- package/dist/application/page.d.ts +14 -7
- package/dist/application/page.js +5 -3
- package/dist/application/page.js.map +1 -1
- package/dist/application/route-graph.d.ts +1 -1
- package/dist/application/route-graph.js.map +1 -1
- package/dist/application/server-fn.d.ts +10 -6
- package/dist/application/server-fn.js +9 -4
- package/dist/application/server-fn.js.map +1 -1
- package/dist/build/dev.d.ts +11 -11
- package/dist/build/dev.js +21 -5
- package/dist/build/dev.js.map +1 -1
- package/dist/cli.js +2 -0
- package/dist/cli.js.map +1 -1
- package/dist/client/application.js +4 -1
- package/dist/client/application.js.map +1 -1
- package/dist/client/browser-render-status.d.ts +25 -0
- package/dist/client/browser-render-status.js +20 -0
- package/dist/client/browser-render-status.js.map +1 -0
- package/dist/client/call-server.js +7 -2
- package/dist/client/call-server.js.map +1 -1
- package/dist/client/react-dom-renderer.d.ts +3 -2
- package/dist/client/react-dom-renderer.js +43 -2
- package/dist/client/react-dom-renderer.js.map +1 -1
- package/dist/client/route-refresh.d.ts +1 -2
- package/dist/client/route-refresh.js +5 -18
- package/dist/client/route-refresh.js.map +1 -1
- package/dist/dev/client.d.ts +2 -1
- package/dist/dev/client.js +32 -5
- package/dist/dev/client.js.map +1 -1
- package/dist/dev/panel.d.ts +6 -1
- package/dist/dev/panel.js +18 -3
- package/dist/dev/panel.js.map +1 -1
- package/dist/dev/runtime-failure.d.ts +1 -0
- package/dist/dev/runtime-failure.js +7 -1
- package/dist/dev/runtime-failure.js.map +1 -1
- package/dist/rsc/render-route-tree.d.ts +3 -3
- package/dist/rsc/render-route-tree.js +2 -2
- package/dist/rsc/render-route-tree.js.map +1 -1
- package/dist/server/application.js +54 -35
- package/dist/server/application.js.map +1 -1
- package/dist/server/server-fn-request.js +3 -1
- package/dist/server/server-fn-request.js.map +1 -1
- package/docs/02-guides/01-server-functions/50_greet.ts +18 -0
- package/docs/02-guides/01-server-functions/60_greeting-form.tsx +23 -0
- package/docs/02-guides/01-server-functions/index.md +7 -0
- package/docs/02-guides/03-routing/index.md +5 -1
- package/docs/03-advanced/03-server-function-execution-and-refresh/index.md +3 -1
- package/docs/04-api-reference/02-page/index.md +8 -3
- package/docs/04-api-reference/06-middleware/index.md +2 -2
- package/docs/04-api-reference/08-server-fn/index.md +23 -0
- package/package.json +1 -1
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Effect, Layer } from "effect";
|
|
2
2
|
import { checkBrowserCapabilities } 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,7 +44,8 @@ import * as __rspack_external__effect_platform_browser_BrowserHttpClient_04160dc
|
|
|
42
44
|
|
|
43
45
|
|
|
44
46
|
|
|
45
|
-
|
|
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
51
|
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"];
|
|
@@ -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":"
|
|
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 { 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 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","BrowserRenderStatus","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;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,aAAaA,GAAG,CAACC,aACrBC,QAAQ,GAAG,CAAC,QAAQ,KAAK,gBACrBrB,iBAAiB,CACf,CAAC,oEAAoE,EAAEoB,WAAW,sEAAsE,CAAC,IAE3JpB,cAAW;AAEjB,MAAMsB,wBAAwBA,GAAGtB,UAAU,CAAC;IAC1C,MAAMuB,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,GAAG3B,aAAa,CACtCA,UAAU,CAAC;IACT,IAAIqB,QAAQ,GAAG,CAAC,QAAQ,KAAK,eAAe;QAC1C,OAAOrB,iBAAiB,CAAC,IAAM,0BAAuB,EAAE,IAAI,CAC1DA,cAAc,CAAC,CAAC,EAAE4B,cAAc,EAAE,GAAKA,iBACvC5B,eAAY,CAAC,CAAC6B,QAAU7B,eAAe,CAAC,+BAA+B6B,SACvE7B,iBAAiB;IAErB;IAEA,OAAOE,6BAA6B,CAClCF,cAAc,CAACsB,wBAAwBA,GACvCtB,gBAAgB,CAAC;QACf,iBAAiB,IAAMkB,oBAAoBA;QAC3C,wBAAwB,IAAMA,oBAAoBA;QAClD,+BAA+B,IAAMC,aAAaA,CAAC;QACnD,qCAAqC,IAAMA,aAAaA,CAAC;IAC3D;IAEF,OAAO,OAAOnB,YAAY;AAC5B,IACA,IAAI,CAACA,cAAc,CAACiB,YAAYA,GAAG"}
|
|
@@ -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
|
-
|
|
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
|
|
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":";;;;;;;;;;;;;;;;;;;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,IAAIC,gBAAgBhB,oBAAoBC;QACxC,IAAIe,kBAAkB,YAAY;YAChC,OAAOlB,eAAe,4BAA4B;YAClD,sFAAsF;YACtFkB,gBAAgBhB,oBAAoBC;QACtC;QAEA,IAAIe,kBAAkB,gBAAgB;YACpC,OAAOL,SAAS,OAAO;YACvB,OAAOb,eAAe,mBAAmB,CAAC;YAC1C;QACF;QACA,MAAMmB,gBAAgBpB,YAAY,cAAc,CAACc,SAAS,OAAO,CAAC,SAAS;QAC3E,IAAIO;QACJ,OAAOzC,WAAW,CAAC;YACjBI,eAAeA,CAAC;gBACdD,iBAAiBA,CAAC;gBAClB,2FAA2F;gBAC3F,iCAAiC;gBACjCsC,kBAAkBzB,gBAAgB,OAAO,CAACkB,SAAS,OAAO,CAAC,SAAS;YACtE;QACF;QACA,OAAOlC,UAAU,CAAC;YAACkC,SAAS,SAAS;YAAElC,cAAc,CAAC,IAAMyC;SAAiB,EAAE;YAC7E,aAAa;YACb,SAAS;QACX,GAAG,IAAI,CACLzC,cAAc,CAACA,WAAW,CAACwC,iBAC3BxC,cAAc,CAAC,IAAMkC,SAAS,OAAO,GACrClC,iBAAiB;IAErB;IAEA,OAAOA,WAAW,CAAC;QACjBO,iBAAiBA,CAAC,CAACoB,IAAIC;YACrB,MAAMC,mBAAmBa,QAAQ,aAAa;YAC9C,KAAKxB,IAAIQ,WAAWC,IAAIC,MAAMC,mBAAmB,KAAK,CAACA,iBAAiB,MAAM;YAC9E,OAAOA,iBAAiB,OAAO;QACjC;IACF;AACF,GAAG"}
|
|
@@ -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: '
|
|
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.
|
|
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
|
|
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":"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"}
|
|
@@ -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,
|
|
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
|
|
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
|
-
|
|
123
|
-
|
|
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
|
|
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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"}
|
package/dist/dev/client.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import { Effect } from 'effect';
|
|
2
|
+
import { BrowserRenderStatus } from '../client/browser-render-status.js';
|
|
2
3
|
import { RouteRefresher } from '../client/route-refresh.js';
|
|
3
|
-
export declare const startDevClient: Effect.Effect<void, import("effect/unstable/rpc/RpcClientError").RpcClientError, RouteRefresher | import("effect/Scope").Scope>;
|
|
4
|
+
export declare const startDevClient: Effect.Effect<void, import("effect/unstable/rpc/RpcClientError").RpcClientError, BrowserRenderStatus | RouteRefresher | import("effect/Scope").Scope>;
|
package/dist/dev/client.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { Effect, Layer, Ref, Semaphore, Stream } from "effect";
|
|
2
2
|
import { RpcClient, RpcSerialization } from "effect/unstable/rpc";
|
|
3
|
+
import { BrowserRenderStatus } from "../client/browser-render-status.js";
|
|
3
4
|
import { RouteRefresher } from "../client/route-refresh.js";
|
|
4
5
|
import { DevChannelPath, DevRpcs } from "./channel.js";
|
|
5
6
|
import { decideHotUpdate } from "./hmr-update.js";
|
|
6
7
|
import { makeDevPanel } from "./panel.js";
|
|
7
|
-
import { reportBrowserFailures } from "./runtime-failure.js";
|
|
8
|
+
import { fromRenderError, reportBrowserFailures } from "./runtime-failure.js";
|
|
8
9
|
|
|
9
10
|
import * as __rspack_external__effect_platform_browser_BrowserSocket_31178d5b from "@effect/platform-browser/BrowserSocket";
|
|
10
11
|
|
|
@@ -23,6 +24,8 @@ import * as __rspack_external__effect_platform_browser_BrowserSocket_31178d5b fr
|
|
|
23
24
|
|
|
24
25
|
|
|
25
26
|
|
|
27
|
+
|
|
28
|
+
|
|
26
29
|
const socketProtocol = location.protocol === 'https:' ? 'wss:' : 'ws:';
|
|
27
30
|
const socketUrl = `${socketProtocol}//${location.host}${DevChannelPath}`;
|
|
28
31
|
const DevRpcProtocolLayer = RpcClient.layerProtocolSocket({
|
|
@@ -88,6 +91,7 @@ const settlePendingUpdate = Effect.fnUntraced(function*(pendingUpdate) {
|
|
|
88
91
|
});
|
|
89
92
|
const startDevClient = Effect.gen(function*() {
|
|
90
93
|
const routeRefresher = yield* RouteRefresher;
|
|
94
|
+
const renderStatus = yield* BrowserRenderStatus;
|
|
91
95
|
const panel = yield* makeDevPanel;
|
|
92
96
|
const pendingUpdate = yield* Ref.make({
|
|
93
97
|
acknowledgedClientHash: import.meta.rspackHash,
|
|
@@ -101,13 +105,31 @@ const startDevClient = Effect.gen(function*() {
|
|
|
101
105
|
if (action === 'Reloading') {
|
|
102
106
|
return;
|
|
103
107
|
}
|
|
104
|
-
|
|
108
|
+
const status = yield* renderStatus.get;
|
|
109
|
+
if (action === 'Refresh' || status._tag === 'Failed') {
|
|
105
110
|
yield* routeRefresher.refreshCurrentRoute('hmr-refresh');
|
|
111
|
+
// Starting a refresh is not evidence that the replacement rendered successfully.
|
|
112
|
+
return;
|
|
106
113
|
}
|
|
107
114
|
yield* panel.dispatch({
|
|
108
|
-
_tag: '
|
|
115
|
+
_tag: 'RuntimeReconciled'
|
|
109
116
|
});
|
|
110
117
|
});
|
|
118
|
+
yield* renderStatus.changes.pipe(Stream.runForEach((status)=>{
|
|
119
|
+
switch(status._tag){
|
|
120
|
+
case 'Waiting':
|
|
121
|
+
return Effect["void"];
|
|
122
|
+
case 'Rendered':
|
|
123
|
+
return panel.dispatch({
|
|
124
|
+
_tag: 'RuntimeReconciled'
|
|
125
|
+
});
|
|
126
|
+
case 'Failed':
|
|
127
|
+
return panel.dispatch({
|
|
128
|
+
_tag: 'RenderFailed',
|
|
129
|
+
failure: fromRenderError(status.error, status.componentStack)
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
}), Effect.forkScoped);
|
|
111
133
|
yield* reportBrowserFailures((failure)=>panel.dispatch({
|
|
112
134
|
_tag: 'RuntimeFailed',
|
|
113
135
|
failure
|
|
@@ -117,8 +139,13 @@ const startDevClient = Effect.gen(function*() {
|
|
|
117
139
|
const handleUpdate = Effect.fnUntraced(function*(phase, message) {
|
|
118
140
|
if (message._tag === 'BuildFailed') {
|
|
119
141
|
yield* panel.dispatch(message);
|
|
120
|
-
} else
|
|
121
|
-
yield*
|
|
142
|
+
} else {
|
|
143
|
+
yield* panel.dispatch({
|
|
144
|
+
_tag: 'BuildSucceeded'
|
|
145
|
+
});
|
|
146
|
+
if (phase === 'Live' || message.clientHash !== import.meta.rspackHash) {
|
|
147
|
+
yield* handleHotUpdate(message);
|
|
148
|
+
}
|
|
122
149
|
}
|
|
123
150
|
return 'Live';
|
|
124
151
|
});
|
package/dist/dev/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dev/client.js","sources":["../../src/dev/client.ts"],"sourcesContent":["import * as BrowserSocket from '@effect/platform-browser/BrowserSocket';\nimport { Effect, Layer, Ref, Semaphore, Stream } from 'effect';\nimport { RpcClient, RpcSerialization } from 'effect/unstable/rpc';\n\nimport { RouteRefresher } from '../client/route-refresh';\nimport { DevChannelPath, DevRpcs, type DevUpdate } from './channel';\nimport { decideHotUpdate, type HotUpdateCheck, type PendingDevUpdate } from './hmr-update';\nimport { makeDevPanel } from './panel';\nimport { reportBrowserFailures } from './runtime-failure';\n\nconst socketProtocol = location.protocol === 'https:' ? 'wss:' : 'ws:';\nconst socketUrl = `${socketProtocol}//${location.host}${DevChannelPath}`;\n\nconst DevRpcProtocolLayer = RpcClient.layerProtocolSocket({ retryTransientErrors: true }).pipe(\n Layer.provide(BrowserSocket.layerWebSocket(socketUrl)),\n Layer.provide(RpcSerialization.layerJson),\n);\n\ntype DevHotUpdate = Exclude<DevUpdate, { readonly _tag: 'BuildFailed' }>;\ntype DevUpdatePhase = 'Snapshot' | 'Live';\n\nconst recordUpdate = (pending: PendingDevUpdate, message: DevHotUpdate): PendingDevUpdate => ({\n acknowledgedClientHash: pending.acknowledgedClientHash,\n clientHash: message.clientHash,\n rscRefresh:\n pending.rscRefresh === 'Pending' || message._tag === 'RscUpdate' ? 'Pending' : 'Current',\n});\n\nconst settlePendingUpdate = Effect.fnUntraced(function* (pendingUpdate: Ref.Ref<PendingDevUpdate>) {\n const reconcile = Effect.gen(function* () {\n const pending = yield* Ref.get(pendingUpdate);\n if (pending.acknowledgedClientHash !== pending.clientHash) {\n const previousHash = import.meta.rspackHash;\n const check = yield* Effect.tryPromise(() => import.meta.webpackHot!.check(true)).pipe(\n Effect.match({\n onFailure: (): HotUpdateCheck => ({ _tag: 'Failed' }),\n onSuccess: (updatedModules): HotUpdateCheck => ({\n _tag: 'Completed',\n currentHash: import.meta.rspackHash,\n previousHash,\n updatedModules,\n }),\n }),\n );\n const decision = decideHotUpdate(pending, check);\n if (decision._tag === 'Reload') {\n yield* Effect.sync(() => location.reload());\n return 'Reloading' as const;\n }\n\n yield* Ref.update(pendingUpdate, (current) => ({\n ...current,\n acknowledgedClientHash: decision.acknowledgedClientHash,\n }));\n return 'Retry' as const;\n }\n\n return yield* Ref.modify(pendingUpdate, (current) => {\n if (current.acknowledgedClientHash !== current.clientHash) {\n return ['Retry', current] as const;\n }\n if (current.rscRefresh === 'Current') {\n return ['Current', current] as const;\n }\n return ['Refresh', { ...current, rscRefresh: 'Current' }] as const;\n });\n });\n\n return yield* reconcile.pipe(Effect.repeat({ while: (action) => action === 'Retry' }));\n});\n\nexport const startDevClient = Effect.gen(function* () {\n const routeRefresher = yield* RouteRefresher;\n const panel = yield* makeDevPanel;\n const pendingUpdate = yield* Ref.make<PendingDevUpdate>({\n acknowledgedClientHash: import.meta.rspackHash,\n clientHash: import.meta.rspackHash,\n rscRefresh: 'Current',\n });\n const updateLock = yield* Semaphore.make(1);\n const handleHotUpdate = Effect.fnUntraced(function* (message: DevHotUpdate) {\n yield* Ref.update(pendingUpdate, (pending) => recordUpdate(pending, message));\n const action = yield* updateLock.withPermit(settlePendingUpdate(pendingUpdate));\n if (action === 'Reloading') {\n return;\n }\n if (action === 'Refresh') {\n yield* routeRefresher.refreshCurrentRoute('hmr-refresh');\n }\n yield* panel.dispatch({ _tag: '
|
|
1
|
+
{"version":3,"file":"dev/client.js","sources":["../../src/dev/client.ts"],"sourcesContent":["import * as BrowserSocket from '@effect/platform-browser/BrowserSocket';\nimport { Effect, Layer, Ref, Semaphore, Stream } from 'effect';\nimport { RpcClient, RpcSerialization } from 'effect/unstable/rpc';\n\nimport { BrowserRenderStatus } from '../client/browser-render-status';\nimport { RouteRefresher } from '../client/route-refresh';\nimport { DevChannelPath, DevRpcs, type DevUpdate } from './channel';\nimport { decideHotUpdate, type HotUpdateCheck, type PendingDevUpdate } from './hmr-update';\nimport { makeDevPanel } from './panel';\nimport { fromRenderError, reportBrowserFailures } from './runtime-failure';\n\nconst socketProtocol = location.protocol === 'https:' ? 'wss:' : 'ws:';\nconst socketUrl = `${socketProtocol}//${location.host}${DevChannelPath}`;\n\nconst DevRpcProtocolLayer = RpcClient.layerProtocolSocket({ retryTransientErrors: true }).pipe(\n Layer.provide(BrowserSocket.layerWebSocket(socketUrl)),\n Layer.provide(RpcSerialization.layerJson),\n);\n\ntype DevHotUpdate = Exclude<DevUpdate, { readonly _tag: 'BuildFailed' }>;\ntype DevUpdatePhase = 'Snapshot' | 'Live';\n\nconst recordUpdate = (pending: PendingDevUpdate, message: DevHotUpdate): PendingDevUpdate => ({\n acknowledgedClientHash: pending.acknowledgedClientHash,\n clientHash: message.clientHash,\n rscRefresh:\n pending.rscRefresh === 'Pending' || message._tag === 'RscUpdate' ? 'Pending' : 'Current',\n});\n\nconst settlePendingUpdate = Effect.fnUntraced(function* (pendingUpdate: Ref.Ref<PendingDevUpdate>) {\n const reconcile = Effect.gen(function* () {\n const pending = yield* Ref.get(pendingUpdate);\n if (pending.acknowledgedClientHash !== pending.clientHash) {\n const previousHash = import.meta.rspackHash;\n const check = yield* Effect.tryPromise(() => import.meta.webpackHot!.check(true)).pipe(\n Effect.match({\n onFailure: (): HotUpdateCheck => ({ _tag: 'Failed' }),\n onSuccess: (updatedModules): HotUpdateCheck => ({\n _tag: 'Completed',\n currentHash: import.meta.rspackHash,\n previousHash,\n updatedModules,\n }),\n }),\n );\n const decision = decideHotUpdate(pending, check);\n if (decision._tag === 'Reload') {\n yield* Effect.sync(() => location.reload());\n return 'Reloading' as const;\n }\n\n yield* Ref.update(pendingUpdate, (current) => ({\n ...current,\n acknowledgedClientHash: decision.acknowledgedClientHash,\n }));\n return 'Retry' as const;\n }\n\n return yield* Ref.modify(pendingUpdate, (current) => {\n if (current.acknowledgedClientHash !== current.clientHash) {\n return ['Retry', current] as const;\n }\n if (current.rscRefresh === 'Current') {\n return ['Current', current] as const;\n }\n return ['Refresh', { ...current, rscRefresh: 'Current' }] as const;\n });\n });\n\n return yield* reconcile.pipe(Effect.repeat({ while: (action) => action === 'Retry' }));\n});\n\nexport const startDevClient = Effect.gen(function* () {\n const routeRefresher = yield* RouteRefresher;\n const renderStatus = yield* BrowserRenderStatus;\n const panel = yield* makeDevPanel;\n const pendingUpdate = yield* Ref.make<PendingDevUpdate>({\n acknowledgedClientHash: import.meta.rspackHash,\n clientHash: import.meta.rspackHash,\n rscRefresh: 'Current',\n });\n const updateLock = yield* Semaphore.make(1);\n const handleHotUpdate = Effect.fnUntraced(function* (message: DevHotUpdate) {\n yield* Ref.update(pendingUpdate, (pending) => recordUpdate(pending, message));\n const action = yield* updateLock.withPermit(settlePendingUpdate(pendingUpdate));\n if (action === 'Reloading') {\n return;\n }\n const status = yield* renderStatus.get;\n if (action === 'Refresh' || status._tag === 'Failed') {\n yield* routeRefresher.refreshCurrentRoute('hmr-refresh');\n // Starting a refresh is not evidence that the replacement rendered successfully.\n return;\n }\n yield* panel.dispatch({ _tag: 'RuntimeReconciled' });\n });\n yield* renderStatus.changes.pipe(\n Stream.runForEach((status) => {\n switch (status._tag) {\n case 'Waiting':\n return Effect.void;\n case 'Rendered':\n return panel.dispatch({ _tag: 'RuntimeReconciled' });\n case 'Failed':\n return panel.dispatch({\n _tag: 'RenderFailed',\n failure: fromRenderError(status.error, status.componentStack),\n });\n }\n }),\n Effect.forkScoped,\n );\n yield* reportBrowserFailures((failure) =>\n panel.dispatch({ _tag: 'RuntimeFailed', failure }),\n ).pipe(Effect.forkScoped);\n\n const observeUpdates = Effect.gen(function* () {\n const client = yield* RpcClient.make(DevRpcs);\n const handleUpdate = Effect.fnUntraced(function* (phase: DevUpdatePhase, message: DevUpdate) {\n if (message._tag === 'BuildFailed') {\n yield* panel.dispatch(message);\n } else {\n yield* panel.dispatch({ _tag: 'BuildSucceeded' });\n if (phase === 'Live' || message.clientHash !== import.meta.rspackHash) {\n yield* handleHotUpdate(message);\n }\n }\n\n return 'Live' as const;\n });\n\n yield* client.ObserveDevUpdates().pipe(\n Stream.runFoldEffect((): DevUpdatePhase => 'Snapshot', handleUpdate),\n Effect.asVoid,\n );\n });\n\n yield* observeUpdates.pipe(Effect.provide(DevRpcProtocolLayer));\n});\n"],"names":["BrowserSocket","Effect","Layer","Ref","Semaphore","Stream","RpcClient","RpcSerialization","BrowserRenderStatus","RouteRefresher","DevChannelPath","DevRpcs","decideHotUpdate","makeDevPanel","fromRenderError","reportBrowserFailures","socketProtocol","location","socketUrl","DevRpcProtocolLayer","recordUpdate","pending","message","settlePendingUpdate","pendingUpdate","reconcile","previousHash","check","updatedModules","decision","current","action","startDevClient","routeRefresher","renderStatus","panel","updateLock","handleHotUpdate","status","failure","observeUpdates","client","handleUpdate","phase"],"mappings":"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"}
|
package/dist/dev/panel.d.ts
CHANGED
|
@@ -25,7 +25,12 @@ export type DevPanelEvent = {
|
|
|
25
25
|
readonly _tag: 'RuntimeFailed';
|
|
26
26
|
readonly failure: DevRuntimeFailure;
|
|
27
27
|
} | {
|
|
28
|
-
readonly _tag: '
|
|
28
|
+
readonly _tag: 'RenderFailed';
|
|
29
|
+
readonly failure: DevRuntimeFailure;
|
|
30
|
+
} | {
|
|
31
|
+
readonly _tag: 'BuildSucceeded';
|
|
32
|
+
} | {
|
|
33
|
+
readonly _tag: 'RuntimeReconciled';
|
|
29
34
|
} | {
|
|
30
35
|
readonly _tag: 'Opened';
|
|
31
36
|
} | {
|