effective-rsc 0.1.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LLMS.md CHANGED
@@ -183,9 +183,12 @@ Give work that must outlive a request an explicit application-owned scope.
183
183
 
184
184
  ERSC handles eligible document navigations through the browser Navigation API and
185
185
  `NavigationPrecommitController`. There is no History API fallback. A browser missing either one
186
- never hydrates: the streamed document stays as served and the application behaves as a plain
187
- multi-page application, with document navigations and natively submitted forms but no interactive
188
- Client Components.
186
+ still hydrates Client Components and supports Server Functions and streamed current-page refreshes,
187
+ including HMR. Links use full-page navigation instead of the client router. Without JavaScript,
188
+ the server-rendered document retains working links and natively submitted forms.
189
+
190
+ Development reports a missing navigation API in the console and a dismissible development-panel
191
+ warning. The warning does not block interaction and is absent in production.
189
192
 
190
193
  An intercepted Page navigation has two milestones:
191
194
 
package/README.md CHANGED
@@ -24,9 +24,9 @@ native RSC support.
24
24
  ## Requirements
25
25
 
26
26
  - Bun 1.4 or newer is the only supported server runtime.
27
- - Client navigation and hydration require the Navigation API and `NavigationPrecommitController`;
28
- there is no History API fallback. Browsers without them receive the server-rendered document as a
29
- plain multi-page application, with working links and natively submitted forms.
27
+ - Client navigation requires the Navigation API and `NavigationPrecommitController`; there is no
28
+ History API fallback. Browsers without them still hydrate Client Components and support Server
29
+ Functions, but links use full-page navigation. Without JavaScript, links and native forms still work.
30
30
  - React, React DOM, Effect, Effect's browser and Bun platforms, and
31
31
  `react-server-dom-rspack` must use the exact compatible versions shown below.
32
32
 
@@ -1,5 +1,5 @@
1
1
  import { Effect, Layer } from "effect";
2
- import { checkBrowserCapabilities } from "./browser-capabilities.js";
2
+ import { navigationMode } from "./browser-capabilities.js";
3
3
  import { BrowserEffectRunner } from "./browser-effect-runner.js";
4
4
  import { BrowserRenderStatus } from "./browser-render-status.js";
5
5
  import { BrowserRenderer } from "./browser-renderer.js";
@@ -48,25 +48,25 @@ import * as __rspack_external__effect_platform_browser_BrowserHttpClient_04160dc
48
48
  const BrowserServicesLayer = Layer.mergeAll(BrowserEffectRunner.layer, BrowserRenderer.layer, BrowserRenderStatus.layer, FlightClient.layer, NavigationApi.layer).pipe(Layer.provide(InitialFlightStream.layer));
49
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));
50
50
  const renderBrowserFailure = Effect.sync(showBrowserFailure);
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"];
52
- const activateClientNavigation = Effect.gen(function*() {
51
+ const activateBrowser = Effect.gen(function*() {
53
52
  const routeLoader = yield* RouteLoader;
54
53
  const reactDOMRenderer = yield* ReactDOMRenderer;
55
54
  const initialPayload = yield* routeLoader.loadInitial;
56
55
  yield* reactDOMRenderer.hydrate(document, initialPayload);
57
- yield* installClientRouter;
58
56
  yield* installRouteRefresh;
59
57
  yield* installCallServer;
58
+ const mode = yield* navigationMode;
59
+ if (mode === 'Client') {
60
+ yield* installClientRouter;
61
+ }
60
62
  });
61
63
  const browserMain = Effect.scoped(Effect.gen(function*() {
62
64
  if (process.env.NODE_ENV === 'development') {
63
65
  yield* Effect.tryPromise(()=>import("../dev/client.js")).pipe(Effect.flatMap(({ startDevClient })=>startDevClient), Effect["catch"]((cause)=>Effect.logError('Development client stopped.', cause)), Effect.forkScoped);
64
66
  }
65
- yield* checkBrowserCapabilities.pipe(Effect.andThen(activateClientNavigation), Effect.catchTags({
67
+ yield* activateBrowser.pipe(Effect.catchTags({
66
68
  FlightLoadError: ()=>renderBrowserFailure,
67
- ReactDOMHydrationError: ()=>renderBrowserFailure,
68
- NavigationApiUnavailableError: ()=>skipHydration('the Navigation API'),
69
- NavigationPrecommitUnavailableError: ()=>skipHydration('NavigationPrecommitController')
69
+ ReactDOMHydrationError: ()=>renderBrowserFailure
70
70
  }));
71
71
  return yield* Effect.never;
72
72
  })).pipe(Effect.provide(BrowserLayer));
@@ -1 +1 @@
1
- {"version":3,"file":"client/application.js","sources":["../../src/client/application.ts"],"sourcesContent":["// oxlint-disable effecttsgo/process-env, effecttsgo/process-env-in-effect -- Rspack replaces NODE_ENV at compile time.\nimport * as BrowserHttpClient from '@effect/platform-browser/BrowserHttpClient';\nimport { Effect, Layer } from 'effect';\n\nimport { checkBrowserCapabilities } from './browser-capabilities';\nimport { BrowserEffectRunner } from './browser-effect-runner';\nimport { 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"}
1
+ {"version":3,"file":"client/application.js","sources":["../../src/client/application.ts"],"sourcesContent":["// oxlint-disable effecttsgo/process-env, effecttsgo/process-env-in-effect -- Rspack replaces NODE_ENV at compile time.\nimport * as BrowserHttpClient from '@effect/platform-browser/BrowserHttpClient';\nimport { Effect, Layer } from 'effect';\n\nimport { navigationMode } from './browser-capabilities';\nimport { BrowserEffectRunner } from './browser-effect-runner';\nimport { BrowserRenderStatus } from './browser-render-status';\nimport { BrowserRenderer } from './browser-renderer';\nimport { showBrowserFailure } from './browser-screen';\nimport { installCallServer } from './call-server';\nimport { installClientRouter } from './client-router';\nimport { FlightClient } from './flight-client';\nimport { InitialFlightStream } from './initial-flight-stream';\nimport { NavigationApi } from './navigation-api';\nimport { ReactDOMRenderer } from './react-dom-renderer';\nimport { RouteLoader } from './route-loader';\nimport { installRouteRefresh, RouteRefresher } from './route-refresh';\n\nconst BrowserServicesLayer = Layer.mergeAll(\n BrowserEffectRunner.layer,\n BrowserRenderer.layer,\n BrowserRenderStatus.layer,\n FlightClient.layer,\n NavigationApi.layer,\n).pipe(Layer.provide(InitialFlightStream.layer));\n\nconst BrowserLayer = Layer.mergeAll(\n ReactDOMRenderer.layer,\n RouteLoader.layer,\n RouteRefresher.layer,\n).pipe(Layer.provideMerge(BrowserServicesLayer), Layer.provide(BrowserHttpClient.layerFetch));\n\nconst renderBrowserFailure = Effect.sync(showBrowserFailure);\n\nconst activateBrowser = Effect.gen(function* () {\n const routeLoader = yield* RouteLoader;\n const reactDOMRenderer = yield* ReactDOMRenderer;\n\n const initialPayload = yield* routeLoader.loadInitial;\n yield* reactDOMRenderer.hydrate(document, initialPayload);\n yield* installRouteRefresh;\n yield* installCallServer;\n const mode = yield* navigationMode;\n if (mode === 'Client') {\n yield* installClientRouter;\n }\n});\n\nexport const browserMain = Effect.scoped(\n Effect.gen(function* () {\n if (process.env.NODE_ENV === 'development') {\n yield* Effect.tryPromise(() => import('../dev/client')).pipe(\n Effect.flatMap(({ startDevClient }) => startDevClient),\n Effect.catch((cause) => Effect.logError('Development client stopped.', cause)),\n Effect.forkScoped,\n );\n }\n\n yield* activateBrowser.pipe(\n Effect.catchTags({\n FlightLoadError: () => renderBrowserFailure,\n ReactDOMHydrationError: () => renderBrowserFailure,\n }),\n );\n return yield* Effect.never;\n }),\n).pipe(Effect.provide(BrowserLayer));\n"],"names":["BrowserHttpClient","Effect","Layer","navigationMode","BrowserEffectRunner","BrowserRenderStatus","BrowserRenderer","showBrowserFailure","installCallServer","installClientRouter","FlightClient","InitialFlightStream","NavigationApi","ReactDOMRenderer","RouteLoader","installRouteRefresh","RouteRefresher","BrowserServicesLayer","BrowserLayer","renderBrowserFailure","activateBrowser","routeLoader","reactDOMRenderer","initialPayload","document","mode","browserMain","process","startDevClient","cause"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,uHAAuH;AACvC;AACzC;AAEiB;AACM;AACA;AACT;AACC;AACJ;AACI;AACP;AACe;AACb;AACO;AACX;AACyB;AAEtE,MAAMiB,oBAAoBA,GAAGf,cAAc,CACzCE,yBAAyB,EACzBE,qBAAqB,EACrBD,yBAAyB,EACzBK,kBAAkB,EAClBE,mBAAmB,EACnB,IAAI,CAACV,aAAa,CAACS,yBAAyB;AAE9C,MAAMO,YAAYA,GAAGhB,cAAc,CACjCW,sBAAsB,EACtBC,iBAAiB,EACjBE,oBAAoB,EACpB,IAAI,CAACd,kBAAkB,CAACe,oBAAoBA,GAAGf,aAAa,CAACF,gFAA4B;AAE3F,MAAMmB,oBAAoBA,GAAGlB,WAAW,CAACM,kBAAkBA;AAE3D,MAAMa,eAAeA,GAAGnB,UAAU,CAAC;IACjC,MAAMoB,cAAc,OAAOP,WAAWA;IACtC,MAAMQ,mBAAmB,OAAOT,gBAAgBA;IAEhD,MAAMU,iBAAiB,OAAOF,YAAY,WAAW;IACrD,OAAOC,iBAAiB,OAAO,CAACE,UAAUD;IAC1C,OAAOR,mBAAmBA;IAC1B,OAAOP,iBAAiBA;IACxB,MAAMiB,OAAO,OAAOtB,cAAcA;IAClC,IAAIsB,SAAS,UAAU;QACrB,OAAOhB,mBAAmBA;IAC5B;AACF;AAEO,MAAMiB,WAAWA,GAAGzB,aAAa,CACtCA,UAAU,CAAC;IACT,IAAI0B,QAAQ,GAAG,CAAC,QAAQ,KAAK,eAAe;QAC1C,OAAO1B,iBAAiB,CAAC,IAAM,0BAAuB,EAAE,IAAI,CAC1DA,cAAc,CAAC,CAAC,EAAE2B,cAAc,EAAE,GAAKA,iBACvC3B,eAAY,CAAC,CAAC4B,QAAU5B,eAAe,CAAC,+BAA+B4B,SACvE5B,iBAAiB;IAErB;IAEA,OAAOmB,eAAeA,CAAC,IAAI,CACzBnB,gBAAgB,CAAC;QACf,iBAAiB,IAAMkB,oBAAoBA;QAC3C,wBAAwB,IAAMA,oBAAoBA;IACpD;IAEF,OAAO,OAAOlB,YAAY;AAC5B,IACA,IAAI,CAACA,cAAc,CAACiB,YAAYA,GAAG"}
@@ -1,9 +1,2 @@
1
- import { Effect, Schema } from 'effect';
2
- declare const NavigationApiUnavailableError_base: Schema.Class<NavigationApiUnavailableError, Schema.TaggedStruct<"NavigationApiUnavailableError", {}>, import("effect/Cause").YieldableError>;
3
- export declare class NavigationApiUnavailableError extends NavigationApiUnavailableError_base {
4
- }
5
- declare const NavigationPrecommitUnavailableError_base: Schema.Class<NavigationPrecommitUnavailableError, Schema.TaggedStruct<"NavigationPrecommitUnavailableError", {}>, import("effect/Cause").YieldableError>;
6
- export declare class NavigationPrecommitUnavailableError extends NavigationPrecommitUnavailableError_base {
7
- }
8
- export declare const checkBrowserCapabilities: Effect.Effect<undefined, NavigationApiUnavailableError | NavigationPrecommitUnavailableError, never>;
9
- export {};
1
+ import { Effect } from 'effect';
2
+ export declare const navigationMode: Effect.Effect<"Client" | "Document", never, never>;
@@ -1,20 +1,9 @@
1
- import { Effect, Schema } from "effect";
1
+ import { Effect } from "effect";
2
2
 
3
3
 
4
4
 
5
- class NavigationApiUnavailableError extends Schema.TaggedError()('NavigationApiUnavailableError', {}) {
6
- }
7
- class NavigationPrecommitUnavailableError extends Schema.TaggedError()('NavigationPrecommitUnavailableError', {}) {
8
- }
9
- const checkBrowserCapabilities = Effect.gen(function*() {
10
- if (window.navigation === undefined) {
11
- return yield* new NavigationApiUnavailableError();
12
- }
13
- if (window.NavigationPrecommitController === undefined) {
14
- return yield* new NavigationPrecommitUnavailableError();
15
- }
16
- });
5
+ const navigationMode = Effect.sync(()=>window.navigation !== undefined && window.NavigationPrecommitController !== undefined ? 'Client' : 'Document');
17
6
 
18
- export { NavigationApiUnavailableError, NavigationPrecommitUnavailableError, checkBrowserCapabilities };
7
+ export { navigationMode };
19
8
 
20
9
  //# sourceMappingURL=browser-capabilities.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"client/browser-capabilities.js","sources":["../../src/client/browser-capabilities.ts"],"sourcesContent":["import { Effect, Schema } from 'effect';\n\nexport class NavigationApiUnavailableError extends Schema.TaggedError<NavigationApiUnavailableError>()(\n 'NavigationApiUnavailableError',\n {},\n) {}\n\nexport class NavigationPrecommitUnavailableError extends Schema.TaggedError<NavigationPrecommitUnavailableError>()(\n 'NavigationPrecommitUnavailableError',\n {},\n) {}\n\nexport const checkBrowserCapabilities = Effect.gen(function* () {\n if (window.navigation === undefined) {\n return yield* new NavigationApiUnavailableError();\n }\n if (window.NavigationPrecommitController === undefined) {\n return yield* new NavigationPrecommitUnavailableError();\n }\n});\n"],"names":["Effect","Schema","NavigationApiUnavailableError","NavigationPrecommitUnavailableError","checkBrowserCapabilities","window","undefined"],"mappings":";;;AAAwC;AAEjC,MAAME,6BAA6BA,SAASD,kBAAkB,GACnE,iCACA,CAAC;AACA;AAEI,MAAME,mCAAmCA,SAASF,kBAAkB,GACzE,uCACA,CAAC;AACA;AAEI,MAAMG,wBAAwBA,GAAGJ,UAAU,CAAC;IACjD,IAAIK,OAAO,UAAU,KAAKC,WAAW;QACnC,OAAO,OAAO,IAAIJ,6BAA6BA;IACjD;IACA,IAAIG,OAAO,6BAA6B,KAAKC,WAAW;QACtD,OAAO,OAAO,IAAIH,mCAAmCA;IACvD;AACF,GAAG"}
1
+ {"version":3,"file":"client/browser-capabilities.js","sources":["../../src/client/browser-capabilities.ts"],"sourcesContent":["import { Effect } from 'effect';\n\nexport const navigationMode = Effect.sync(() =>\n window.navigation !== undefined && window.NavigationPrecommitController !== undefined\n ? ('Client' as const)\n : ('Document' as const),\n);\n"],"names":["Effect","navigationMode","window","undefined"],"mappings":";;;AAAgC;AAEzB,MAAMC,cAAcA,GAAGD,WAAW,CAAC,IACxCE,OAAO,UAAU,KAAKC,aAAaD,OAAO,6BAA6B,KAAKC,YACvE,WACA,YACL"}
@@ -4,15 +4,16 @@ import { Context, Effect, Layer } from "effect";
4
4
 
5
5
  class NavigationApi extends Context.Service()('ersc/client/NavigationApi', {
6
6
  make: Effect.succeed({
7
- getCurrentEntry: ()=>window.navigation.currentEntry,
7
+ getCurrentEntry: ()=>window.navigation?.currentEntry ?? null,
8
8
  getCurrentUrl: ()=>window.location.href,
9
- getTransition: ()=>window.navigation.transition,
9
+ getTransition: ()=>window.navigation?.transition ?? null,
10
10
  navigate: (url, options)=>window.navigation.navigate(url, options),
11
11
  reloadDocument: ()=>window.location.reload(),
12
12
  replaceDocument: (url)=>window.location.replace(url),
13
13
  subscribe: (listener)=>{
14
- window.navigation.addEventListener('navigate', listener);
15
- return ()=>window.navigation.removeEventListener('navigate', listener);
14
+ const navigation = window.navigation;
15
+ navigation?.addEventListener('navigate', listener);
16
+ return ()=>navigation?.removeEventListener('navigate', listener);
16
17
  }
17
18
  })
18
19
  }) {
@@ -1 +1 @@
1
- {"version":3,"file":"client/navigation-api.js","sources":["../../src/client/navigation-api.ts"],"sourcesContent":["import { Context, Effect, Layer } from 'effect';\n\ntype NavigateListener = (event: NavigateEvent) => void;\n\nexport class NavigationApi extends Context.Service<NavigationApi>()('ersc/client/NavigationApi', {\n make: Effect.succeed({\n getCurrentEntry: () => window.navigation.currentEntry,\n getCurrentUrl: () => window.location.href,\n getTransition: () => window.navigation.transition,\n navigate: (url: string | URL, options?: NavigationNavigateOptions) =>\n window.navigation.navigate(url, options),\n reloadDocument: () => window.location.reload(),\n replaceDocument: (url: string) => window.location.replace(url),\n subscribe: (listener: NavigateListener) => {\n window.navigation.addEventListener('navigate', listener);\n return () => window.navigation.removeEventListener('navigate', listener);\n },\n }),\n}) {\n static readonly layer = Layer.effect(this, this.make);\n\n static readonly layerTest = Layer.mock(this);\n}\n"],"names":["Context","Effect","Layer","NavigationApi","window","url","options","listener"],"mappings":";;;AAAgD;AAIzC,MAAMG,aAAaA,SAASH,eAAe,GAAkB,6BAA6B;IAC/F,MAAMC,cAAc,CAAC;QACnB,iBAAiB,IAAMG,OAAO,UAAU,CAAC,YAAY;QACrD,eAAe,IAAMA,OAAO,QAAQ,CAAC,IAAI;QACzC,eAAe,IAAMA,OAAO,UAAU,CAAC,UAAU;QACjD,UAAU,CAACC,KAAmBC,UAC5BF,OAAO,UAAU,CAAC,QAAQ,CAACC,KAAKC;QAClC,gBAAgB,IAAMF,OAAO,QAAQ,CAAC,MAAM;QAC5C,iBAAiB,CAACC,MAAgBD,OAAO,QAAQ,CAAC,OAAO,CAACC;QAC1D,WAAW,CAACE;YACVH,OAAO,UAAU,CAAC,gBAAgB,CAAC,YAAYG;YAC/C,OAAO,IAAMH,OAAO,UAAU,CAAC,mBAAmB,CAAC,YAAYG;QACjE;IACF;AACF;IACE,OAAgB,QAAQL,YAAY,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE;IAEtD,OAAgB,YAAYA,UAAU,CAAC,IAAI,EAAE;AAC/C"}
1
+ {"version":3,"file":"client/navigation-api.js","sources":["../../src/client/navigation-api.ts"],"sourcesContent":["import { Context, Effect, Layer } from 'effect';\n\ntype NavigateListener = (event: NavigateEvent) => void;\n\nexport class NavigationApi extends Context.Service<NavigationApi>()('ersc/client/NavigationApi', {\n make: Effect.succeed({\n getCurrentEntry: () => window.navigation?.currentEntry ?? null,\n getCurrentUrl: () => window.location.href,\n getTransition: () => window.navigation?.transition ?? null,\n navigate: (url: string | URL, options?: NavigationNavigateOptions) =>\n window.navigation.navigate(url, options),\n reloadDocument: () => window.location.reload(),\n replaceDocument: (url: string) => window.location.replace(url),\n subscribe: (listener: NavigateListener) => {\n const navigation = window.navigation;\n navigation?.addEventListener('navigate', listener);\n return () => navigation?.removeEventListener('navigate', listener);\n },\n }),\n}) {\n static readonly layer = Layer.effect(this, this.make);\n\n static readonly layerTest = Layer.mock(this);\n}\n"],"names":["Context","Effect","Layer","NavigationApi","window","url","options","listener","navigation"],"mappings":";;;AAAgD;AAIzC,MAAMG,aAAaA,SAASH,eAAe,GAAkB,6BAA6B;IAC/F,MAAMC,cAAc,CAAC;QACnB,iBAAiB,IAAMG,OAAO,UAAU,EAAE,gBAAgB;QAC1D,eAAe,IAAMA,OAAO,QAAQ,CAAC,IAAI;QACzC,eAAe,IAAMA,OAAO,UAAU,EAAE,cAAc;QACtD,UAAU,CAACC,KAAmBC,UAC5BF,OAAO,UAAU,CAAC,QAAQ,CAACC,KAAKC;QAClC,gBAAgB,IAAMF,OAAO,QAAQ,CAAC,MAAM;QAC5C,iBAAiB,CAACC,MAAgBD,OAAO,QAAQ,CAAC,OAAO,CAACC;QAC1D,WAAW,CAACE;YACV,MAAMC,aAAaJ,OAAO,UAAU;YACpCI,YAAY,iBAAiB,YAAYD;YACzC,OAAO,IAAMC,YAAY,oBAAoB,YAAYD;QAC3D;IACF;AACF;IACE,OAAgB,QAAQL,YAAY,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE;IAEtD,OAAgB,YAAYA,UAAU,CAAC,IAAI,EAAE;AAC/C"}
@@ -1,5 +1,6 @@
1
- import { Effect, Layer, Ref, Semaphore, Stream } from "effect";
1
+ import { Console, Effect, Layer, Ref, Semaphore, Stream } from "effect";
2
2
  import { RpcClient, RpcSerialization } from "effect/unstable/rpc";
3
+ import { navigationMode } from "../client/browser-capabilities.js";
3
4
  import { BrowserRenderStatus } from "../client/browser-render-status.js";
4
5
  import { RouteRefresher } from "../client/route-refresh.js";
5
6
  import { DevChannelPath, DevRpcs } from "./channel.js";
@@ -26,6 +27,8 @@ import * as __rspack_external__effect_platform_browser_BrowserSocket_31178d5b fr
26
27
 
27
28
 
28
29
 
30
+
31
+
29
32
  const socketProtocol = location.protocol === 'https:' ? 'wss:' : 'ws:';
30
33
  const socketUrl = `${socketProtocol}//${location.host}${DevChannelPath}`;
31
34
  const DevRpcProtocolLayer = RpcClient.layerProtocolSocket({
@@ -93,6 +96,16 @@ const startDevClient = Effect.gen(function*() {
93
96
  const routeRefresher = yield* RouteRefresher;
94
97
  const renderStatus = yield* BrowserRenderStatus;
95
98
  const panel = yield* makeDevPanel;
99
+ const mode = yield* navigationMode;
100
+ if (mode === 'Document') {
101
+ const missingApi = window.navigation === undefined ? 'Navigation API' : 'NavigationPrecommitController';
102
+ const message = `This browser does not support ${missingApi}. Links use full-page navigation. Client Components, Server Functions, and HMR remain enabled.`;
103
+ yield* Console.warn(`[effective-rsc] ${message}`);
104
+ yield* panel.dispatch({
105
+ _tag: 'Warning',
106
+ message
107
+ });
108
+ }
96
109
  const pendingUpdate = yield* Ref.make({
97
110
  acknowledgedClientHash: import.meta.rspackHash,
98
111
  clientHash: import.meta.rspackHash,
@@ -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 { 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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"}
1
+ {"version":3,"file":"dev/client.js","sources":["../../src/dev/client.ts"],"sourcesContent":["import * as BrowserSocket from '@effect/platform-browser/BrowserSocket';\nimport { Console, Effect, Layer, Ref, Semaphore, Stream } from 'effect';\nimport { RpcClient, RpcSerialization } from 'effect/unstable/rpc';\n\nimport { navigationMode } from '../client/browser-capabilities';\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 mode = yield* navigationMode;\n if (mode === 'Document') {\n const missingApi =\n window.navigation === undefined ? 'Navigation API' : 'NavigationPrecommitController';\n const message = `This browser does not support ${missingApi}. Links use full-page navigation. Client Components, Server Functions, and HMR remain enabled.`;\n yield* Console.warn(`[effective-rsc] ${message}`);\n yield* panel.dispatch({ _tag: 'Warning', message });\n }\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","Console","Effect","Layer","Ref","Semaphore","Stream","RpcClient","RpcSerialization","navigationMode","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","mode","missingApi","window","undefined","updateLock","handleHotUpdate","status","failure","observeUpdates","client","handleUpdate","phase"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAAwE;AACA;AACN;AAEF;AACM;AACb;AACW;AACuB;AACpD;AACoC;AAE3E,MAAMkB,cAAcA,GAAGC,SAAS,QAAQ,KAAK,WAAW,SAAS;AACjE,MAAMC,SAASA,GAAG,GAAGF,cAAcA,CAAC,EAAE,EAAEC,SAAS,IAAI,GAAGP,cAAcA,EAAE;AAExE,MAAMS,mBAAmBA,GAAGd,6BAA6B,CAAC;IAAE,sBAAsB;AAAK,GAAG,IAAI,CAC5FJ,aAAa,CAACH,gFAA4B,CAACoB,SAASA,IACpDjB,aAAa,CAACK,0BAA0B;AAM1C,MAAMc,YAAYA,GAAG,CAACC,SAA2BC,UAA6C;QAC5F,wBAAwBD,QAAQ,sBAAsB;QACtD,YAAYC,QAAQ,UAAU;QAC9B,YACED,QAAQ,UAAU,KAAK,aAAaC,QAAQ,IAAI,KAAK,cAAc,YAAY;IACnF;AAEA,MAAMC,mBAAmBA,GAAGvB,iBAAiB,CAAC,UAAWwB,aAAwC;IAC/F,MAAMC,YAAYzB,UAAU,CAAC;QAC3B,MAAMqB,UAAU,OAAOnB,OAAO,CAACsB;QAC/B,IAAIH,QAAQ,sBAAsB,KAAKA,QAAQ,UAAU,EAAE;YACzD,MAAMK,eAAe,YAAY,UAAU;YAC3C,MAAMC,QAAQ,OAAO3B,iBAAiB,CAAC,IAAM,YAAY,UAAU,CAAE,KAAK,CAAC,OAAO,IAAI,CACpFA,YAAY,CAAC;gBACX,WAAW,IAAuB;wBAAE,MAAM;oBAAS;gBACnD,WAAW,CAAC4B,iBAAoC;wBAC9C,MAAM;wBACN,aAAa,YAAY,UAAU;wBACnCF;wBACAE;oBACF;YACF;YAEF,MAAMC,WAAWjB,eAAeA,CAACS,SAASM;YAC1C,IAAIE,SAAS,IAAI,KAAK,UAAU;gBAC9B,OAAO7B,WAAW,CAAC,IAAMiB,SAAS,MAAM;gBACxC,OAAO;YACT;YAEA,OAAOf,UAAU,CAACsB,eAAe,CAACM,UAAa;oBAC7C,GAAGA,OAAO;oBACV,wBAAwBD,SAAS,sBAAsB;gBACzD;YACA,OAAO;QACT;QAEA,OAAO,OAAO3B,UAAU,CAACsB,eAAe,CAACM;YACvC,IAAIA,QAAQ,sBAAsB,KAAKA,QAAQ,UAAU,EAAE;gBACzD,OAAO;oBAAC;oBAASA;iBAAQ;YAC3B;YACA,IAAIA,QAAQ,UAAU,KAAK,WAAW;gBACpC,OAAO;oBAAC;oBAAWA;iBAAQ;YAC7B;YACA,OAAO;gBAAC;gBAAW;oBAAE,GAAGA,OAAO;oBAAE,YAAY;gBAAU;aAAE;QAC3D;IACF;IAEA,OAAO,OAAOL,UAAU,IAAI,CAACzB,aAAa,CAAC;QAAE,OAAO,CAAC+B,SAAWA,WAAW;IAAQ;AACrF;AAEO,MAAMC,cAAcA,GAAGhC,UAAU,CAAC;IACvC,MAAMiC,iBAAiB,OAAOxB,cAAcA;IAC5C,MAAMyB,eAAe,OAAO1B,mBAAmBA;IAC/C,MAAM2B,QAAQ,OAAOtB,YAAYA;IACjC,MAAMuB,OAAO,OAAO7B,cAAcA;IAClC,IAAI6B,SAAS,YAAY;QACvB,MAAMC,aACJC,OAAO,UAAU,KAAKC,YAAY,mBAAmB;QACvD,MAAMjB,UAAU,CAAC,8BAA8B,EAAEe,WAAW,8FAA8F,CAAC;QAC3J,OAAOtC,YAAY,CAAC,CAAC,gBAAgB,EAAEuB,SAAS;QAChD,OAAOa,MAAM,QAAQ,CAAC;YAAE,MAAM;YAAWb;QAAQ;IACnD;IACA,MAAME,gBAAgB,OAAOtB,QAAQ,CAAmB;QACtD,wBAAwB,YAAY,UAAU;QAC9C,YAAY,YAAY,UAAU;QAClC,YAAY;IACd;IACA,MAAMsC,aAAa,OAAOrC,cAAc,CAAC;IACzC,MAAMsC,kBAAkBzC,iBAAiB,CAAC,UAAWsB,OAAqB;QACxE,OAAOpB,UAAU,CAACsB,eAAe,CAACH,UAAYD,YAAYA,CAACC,SAASC;QACpE,MAAMS,SAAS,OAAOS,WAAW,UAAU,CAACjB,mBAAmBA,CAACC;QAChE,IAAIO,WAAW,aAAa;YAC1B;QACF;QACA,MAAMW,SAAS,OAAOR,aAAa,GAAG;QACtC,IAAIH,WAAW,aAAaW,OAAO,IAAI,KAAK,UAAU;YACpD,OAAOT,eAAe,mBAAmB,CAAC;YAC1C,iFAAiF;YACjF;QACF;QACA,OAAOE,MAAM,QAAQ,CAAC;YAAE,MAAM;QAAoB;IACpD;IACA,OAAOD,aAAa,OAAO,CAAC,IAAI,CAC9B9B,iBAAiB,CAAC,CAACsC;QACjB,OAAQA,OAAO,IAAI;YACjB,KAAK;gBACH,OAAO1C,cAAW;YACpB,KAAK;gBACH,OAAOmC,MAAM,QAAQ,CAAC;oBAAE,MAAM;gBAAoB;YACpD,KAAK;gBACH,OAAOA,MAAM,QAAQ,CAAC;oBACpB,MAAM;oBACN,SAASrB,eAAeA,CAAC4B,OAAO,KAAK,EAAEA,OAAO,cAAc;gBAC9D;QACJ;IACF,IACA1C,iBAAiB;IAEnB,OAAOe,qBAAqBA,CAAC,CAAC4B,UAC5BR,MAAM,QAAQ,CAAC;YAAE,MAAM;YAAiBQ;QAAQ,IAChD,IAAI,CAAC3C,iBAAiB;IAExB,MAAM4C,iBAAiB5C,UAAU,CAAC;QAChC,MAAM6C,SAAS,OAAOxC,cAAc,CAACM,OAAOA;QAC5C,MAAMmC,eAAe9C,iBAAiB,CAAC,UAAW+C,KAAqB,EAAEzB,OAAkB;YACzF,IAAIA,QAAQ,IAAI,KAAK,eAAe;gBAClC,OAAOa,MAAM,QAAQ,CAACb;YACxB,OAAO;gBACL,OAAOa,MAAM,QAAQ,CAAC;oBAAE,MAAM;gBAAiB;gBAC/C,IAAIY,UAAU,UAAUzB,QAAQ,UAAU,KAAK,YAAY,UAAU,EAAE;oBACrE,OAAOmB,gBAAgBnB;gBACzB;YACF;YAEA,OAAO;QACT;QAEA,OAAOuB,OAAO,iBAAiB,GAAG,IAAI,CACpCzC,oBAAoB,CAAC,IAAsB,YAAY0C,eACvD9C,aAAa;IAEjB;IAEA,OAAO4C,eAAe,IAAI,CAAC5C,cAAc,CAACmB,mBAAmBA;AAC/D,GAAG"}
@@ -3,6 +3,9 @@ import type { DevRuntimeFailure } from './runtime-failure.js';
3
3
  export type { DevRuntimeFailure } from './runtime-failure.js';
4
4
  export declare const DevPanelElementName = "ersc-dev-panel";
5
5
  type DevPanelContent = {
6
+ readonly _tag: 'Warning';
7
+ readonly message: string;
8
+ } | {
6
9
  readonly _tag: 'BuildFailed';
7
10
  readonly diagnostics: string;
8
11
  } | {
@@ -19,6 +22,9 @@ export type DevPanelState = {
19
22
  readonly content: DevPanelContent;
20
23
  };
21
24
  export type DevPanelEvent = {
25
+ readonly _tag: 'Warning';
26
+ readonly message: string;
27
+ } | {
22
28
  readonly _tag: 'BuildFailed';
23
29
  readonly diagnostics: string;
24
30
  } | {
package/dist/dev/panel.js CHANGED
@@ -9582,7 +9582,7 @@ const ScrollAreaThumb_ScrollAreaThumb = /*#__PURE__*/__rspack_external_react.for
9582
9582
  if (process.env.NODE_ENV !== "production") ScrollAreaThumb_ScrollAreaThumb.displayName = "ScrollAreaThumb";
9583
9583
 
9584
9584
 
9585
- const panelraw_namespaceObject = ":host {\n all: initial;\n color-scheme: dark;\n --ersc-canvas: #111110;\n --ersc-surface: #181817;\n --ersc-raised: #201f1e;\n --ersc-border: #343230;\n --ersc-border-subtle: #292725;\n --ersc-text: #f3f2ef;\n --ersc-text-secondary: #aaa7a1;\n --ersc-text-tertiary: #77736d;\n --ersc-danger: #ff615a;\n --ersc-danger-muted: #3a1f1d;\n --ersc-ease-out: cubic-bezier(0.23, 1, 0.32, 1);\n}\n\n*,\n*::before,\n*::after {\n box-sizing: border-box;\n}\n\nbutton {\n font: inherit;\n}\n\n.backdrop {\n position: fixed;\n inset: 0;\n z-index: 2147483646;\n width: 100vw;\n background: rgb(9 9 8 / 76%);\n backdrop-filter: blur(2px);\n transition: opacity 160ms var(--ersc-ease-out);\n}\n\n.backdrop[data-starting-style],\n.backdrop[data-ending-style] {\n opacity: 0;\n}\n\n.viewport {\n position: fixed;\n inset: 0;\n z-index: 2147483647;\n width: 100vw;\n overflow: auto;\n padding: clamp(24px, 6vh, 72px) 20px;\n font-family:\n Inter,\n ui-sans-serif,\n -apple-system,\n BlinkMacSystemFont,\n 'Segoe UI',\n sans-serif;\n color: var(--ersc-text);\n}\n\n.popup {\n display: flex;\n width: min(920px, 100%);\n max-height: calc(100svh - clamp(48px, 12vh, 144px));\n margin: 0 auto;\n overflow: hidden;\n color: var(--ersc-text);\n background: var(--ersc-canvas);\n border: 1px solid var(--ersc-border);\n border-radius: 10px;\n box-shadow:\n 0 32px 80px rgb(0 0 0 / 48%),\n 0 2px 8px rgb(0 0 0 / 32%);\n flex-direction: column;\n transition:\n opacity 180ms var(--ersc-ease-out),\n transform 180ms var(--ersc-ease-out);\n transform-origin: center;\n}\n\n.popup[data-starting-style],\n.popup[data-ending-style] {\n opacity: 0;\n transform: translateY(8px) scale(0.985);\n}\n\nheader {\n display: flex;\n min-height: 76px;\n gap: 14px;\n align-items: center;\n padding: 14px 16px 14px 18px;\n background: var(--ersc-surface);\n border-bottom: 1px solid var(--ersc-border-subtle);\n}\n\n.error-mark {\n display: grid;\n width: 34px;\n height: 34px;\n color: var(--ersc-danger);\n background: var(--ersc-danger-muted);\n border: 1px solid rgb(255 97 90 / 24%);\n border-radius: 7px;\n flex: 0 0 auto;\n place-items: center;\n}\n\n.error-mark svg {\n width: 18px;\n height: 18px;\n}\n\n.heading {\n display: grid;\n min-width: 0;\n gap: 5px;\n flex: 1;\n}\n\n.brand {\n overflow: hidden;\n color: var(--ersc-text-secondary);\n font:\n 600 10px/1.1 ui-monospace,\n SFMono-Regular,\n Consolas,\n 'Liberation Mono',\n monospace;\n letter-spacing: 0.08em;\n text-overflow: ellipsis;\n text-transform: uppercase;\n white-space: nowrap;\n}\n\n.brand span {\n color: var(--ersc-text-tertiary);\n}\n\n.title {\n margin: 0;\n color: var(--ersc-text);\n font-size: 17px;\n font-weight: 600;\n line-height: 1.2;\n letter-spacing: -0.015em;\n}\n\n.failure-count {\n padding: 5px 8px;\n color: var(--ersc-text-secondary);\n font:\n 500 11px/1 ui-monospace,\n SFMono-Regular,\n Consolas,\n 'Liberation Mono',\n monospace;\n background: var(--ersc-raised);\n border: 1px solid var(--ersc-border);\n border-radius: 999px;\n white-space: nowrap;\n}\n\n.close {\n display: grid;\n width: 34px;\n height: 34px;\n padding: 0;\n color: var(--ersc-text-secondary);\n cursor: pointer;\n background: transparent;\n border: 1px solid transparent;\n border-radius: 7px;\n transition:\n color 140ms ease,\n background-color 140ms ease,\n border-color 140ms ease,\n transform 140ms var(--ersc-ease-out);\n place-items: center;\n}\n\n.close svg {\n width: 18px;\n height: 18px;\n}\n\n.close:focus-visible {\n color: var(--ersc-text);\n outline: 2px solid var(--ersc-danger);\n outline-offset: 2px;\n}\n\n.close:active {\n transform: scale(0.94);\n}\n\n.content {\n position: relative;\n min-height: 0;\n overflow: hidden;\n overscroll-behavior: contain;\n}\n\n.content-viewport {\n max-height: calc(100svh - clamp(124px, calc(12vh + 76px), 220px));\n overscroll-behavior: contain;\n scrollbar-width: none;\n}\n\n.scrollbar {\n position: absolute;\n top: 4px;\n right: 3px;\n bottom: 4px;\n display: flex;\n width: 8px;\n padding: 2px;\n border-radius: 999px;\n opacity: 0;\n transition: opacity 140ms ease;\n user-select: none;\n}\n\n.content[data-scrolling] .scrollbar,\n.scrollbar[data-hovering] {\n opacity: 1;\n}\n\n.scrollbar-thumb {\n width: 100%;\n background: #55514c;\n border-radius: inherit;\n}\n\narticle + article {\n border-top: 1px solid var(--ersc-border-subtle);\n}\n\n.failure {\n display: grid;\n grid-template-columns: 58px minmax(0, 1fr);\n}\n\n.failure-index {\n padding: 23px 12px;\n color: var(--ersc-text-tertiary);\n font:\n 500 11px/1 ui-monospace,\n SFMono-Regular,\n Consolas,\n 'Liberation Mono',\n monospace;\n text-align: center;\n background: var(--ersc-surface);\n border-right: 1px solid var(--ersc-border-subtle);\n}\n\n.failure-body {\n min-width: 0;\n padding: 22px 24px 24px;\n}\n\n.category,\n.component-stack-title,\n.message {\n margin: 0;\n}\n\n.category {\n display: flex;\n gap: 8px;\n align-items: center;\n color: var(--ersc-danger);\n font-size: 11px;\n font-weight: 650;\n line-height: 16px;\n letter-spacing: 0.08em;\n text-transform: uppercase;\n}\n\n.status-dot {\n width: 6px;\n height: 6px;\n background: currentcolor;\n border-radius: 50%;\n box-shadow: 0 0 0 3px rgb(255 97 90 / 12%);\n}\n\n.component-stack-title {\n margin: 0 0 8px;\n color: var(--ersc-text-secondary);\n font-size: 10px;\n font-weight: 600;\n line-height: 16px;\n letter-spacing: 0.08em;\n text-transform: uppercase;\n}\n\n.trace-section {\n margin-top: 18px;\n}\n\n.stack {\n margin-top: 16px;\n}\n\n.stack-trigger {\n display: flex;\n width: fit-content;\n gap: 5px;\n align-items: center;\n padding: 5px 0;\n color: var(--ersc-text-secondary);\n font:\n 600 11px/1.4 ui-monospace,\n SFMono-Regular,\n Consolas,\n 'Liberation Mono',\n monospace;\n cursor: pointer;\n background: transparent;\n border: 0;\n transition:\n color 140ms ease,\n transform 140ms var(--ersc-ease-out);\n}\n\n.stack-trigger:focus-visible {\n color: var(--ersc-text);\n outline: 2px solid var(--ersc-danger);\n outline-offset: 2px;\n}\n\n.stack-trigger:active {\n transform: scale(0.97);\n}\n\n.stack-trigger svg {\n width: 14px;\n height: 14px;\n transition: transform 160ms var(--ersc-ease-out);\n}\n\n.stack-trigger[aria-expanded='true'] svg {\n transform: rotate(90deg);\n}\n\n.stack-panel pre {\n margin-top: 6px;\n}\n\n.message {\n margin-top: 10px;\n color: var(--ersc-text);\n font:\n 500 15px/1.55 ui-monospace,\n SFMono-Regular,\n Consolas,\n 'Liberation Mono',\n monospace;\n letter-spacing: -0.015em;\n overflow-wrap: anywhere;\n}\n\n.error-name {\n color: #ff8c87;\n font-weight: 650;\n}\n\npre {\n margin: 0;\n overflow: auto;\n color: #cbc8c2;\n font:\n 12px/1.65 ui-monospace,\n SFMono-Regular,\n Consolas,\n 'Liberation Mono',\n monospace;\n tab-size: 2;\n word-break: break-word;\n white-space: pre-wrap;\n}\n\n.code-frame,\n.build-output {\n padding: 14px 16px;\n background: var(--ersc-surface);\n border: 1px solid var(--ersc-border-subtle);\n border-radius: 7px;\n}\n\n.build-failure {\n padding: 22px 24px 24px;\n}\n\n.section-heading {\n display: flex;\n gap: 16px;\n align-items: baseline;\n justify-content: space-between;\n margin-bottom: 10px;\n color: var(--ersc-text-secondary);\n font:\n 600 10px/1.4 ui-monospace,\n SFMono-Regular,\n Consolas,\n 'Liberation Mono',\n monospace;\n letter-spacing: 0.08em;\n text-transform: uppercase;\n}\n\n.section-heading span:last-child {\n color: var(--ersc-text-tertiary);\n font-weight: 500;\n letter-spacing: 0;\n text-transform: none;\n}\n\n.build-output {\n color: var(--ersc-text);\n background: #0d0d0c;\n border-left: 2px solid var(--ersc-danger);\n}\n\n@media (hover: hover) and (pointer: fine) {\n .close:hover {\n color: var(--ersc-text);\n background: var(--ersc-raised);\n border-color: var(--ersc-border);\n }\n\n .stack-trigger:hover {\n color: var(--ersc-text);\n }\n}\n\n@media (max-width: 560px) {\n .viewport {\n padding: 12px;\n }\n\n .popup {\n max-height: calc(100svh - 24px);\n }\n\n .content-viewport {\n max-height: calc(100svh - 92px);\n }\n\n header {\n min-height: 68px;\n gap: 10px;\n padding-inline: 12px;\n }\n\n .error-mark,\n .close {\n width: 32px;\n height: 32px;\n }\n\n .failure-count {\n display: none;\n }\n\n .failure {\n grid-template-columns: 38px minmax(0, 1fr);\n }\n\n .failure-index {\n padding-inline: 8px;\n }\n\n .failure-body,\n .build-failure {\n padding: 18px 16px 20px;\n }\n\n .section-heading span:last-child {\n display: none;\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .backdrop,\n .popup,\n .close,\n .stack-trigger,\n .stack-trigger svg,\n .scrollbar {\n transition-duration: 0ms;\n }\n\n .popup[data-starting-style],\n .popup[data-ending-style] {\n transform: none;\n }\n}\n\n.sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n white-space: nowrap;\n border: 0;\n clip: rect(0, 0, 0, 0);\n}\n";
9585
+ const panelraw_namespaceObject = ":host {\n all: initial;\n color-scheme: dark;\n --ersc-canvas: #111110;\n --ersc-surface: #181817;\n --ersc-raised: #201f1e;\n --ersc-border: #343230;\n --ersc-border-subtle: #292725;\n --ersc-text: #f3f2ef;\n --ersc-text-secondary: #aaa7a1;\n --ersc-text-tertiary: #77736d;\n --ersc-danger: #ff615a;\n --ersc-danger-muted: #3a1f1d;\n --ersc-ease-out: cubic-bezier(0.23, 1, 0.32, 1);\n}\n\n*,\n*::before,\n*::after {\n box-sizing: border-box;\n}\n\nbutton {\n font: inherit;\n}\n\n.warning-notice {\n position: fixed;\n right: 16px;\n bottom: 16px;\n z-index: 2147483645;\n display: flex;\n width: min(440px, calc(100% - 32px));\n gap: 12px;\n padding: 16px;\n color: var(--ersc-text);\n font:\n 14px/1.5 system-ui,\n sans-serif;\n background: var(--ersc-surface);\n border: 1px solid #b88a36;\n border-radius: 10px;\n box-shadow: 0 8px 32px rgb(0 0 0 / 24%);\n}\n\n.warning-notice .title {\n margin-top: 6px;\n color: #f3c56b;\n}\n\n.warning-notice p {\n margin: 8px 0 0;\n}\n\n.warning-notice .close {\n flex: 0 0 auto;\n}\n\n.backdrop {\n position: fixed;\n inset: 0;\n z-index: 2147483646;\n width: 100vw;\n background: rgb(9 9 8 / 76%);\n backdrop-filter: blur(2px);\n transition: opacity 160ms var(--ersc-ease-out);\n}\n\n.backdrop[data-starting-style],\n.backdrop[data-ending-style] {\n opacity: 0;\n}\n\n.viewport {\n position: fixed;\n inset: 0;\n z-index: 2147483647;\n width: 100vw;\n overflow: auto;\n padding: clamp(24px, 6vh, 72px) 20px;\n font-family:\n Inter,\n ui-sans-serif,\n -apple-system,\n BlinkMacSystemFont,\n 'Segoe UI',\n sans-serif;\n color: var(--ersc-text);\n}\n\n.popup {\n display: flex;\n width: min(920px, 100%);\n max-height: calc(100svh - clamp(48px, 12vh, 144px));\n margin: 0 auto;\n overflow: hidden;\n color: var(--ersc-text);\n background: var(--ersc-canvas);\n border: 1px solid var(--ersc-border);\n border-radius: 10px;\n box-shadow:\n 0 32px 80px rgb(0 0 0 / 48%),\n 0 2px 8px rgb(0 0 0 / 32%);\n flex-direction: column;\n transition:\n opacity 180ms var(--ersc-ease-out),\n transform 180ms var(--ersc-ease-out);\n transform-origin: center;\n}\n\n.popup[data-starting-style],\n.popup[data-ending-style] {\n opacity: 0;\n transform: translateY(8px) scale(0.985);\n}\n\nheader {\n display: flex;\n min-height: 76px;\n gap: 14px;\n align-items: center;\n padding: 14px 16px 14px 18px;\n background: var(--ersc-surface);\n border-bottom: 1px solid var(--ersc-border-subtle);\n}\n\n.error-mark {\n display: grid;\n width: 34px;\n height: 34px;\n color: var(--ersc-danger);\n background: var(--ersc-danger-muted);\n border: 1px solid rgb(255 97 90 / 24%);\n border-radius: 7px;\n flex: 0 0 auto;\n place-items: center;\n}\n\n.error-mark svg {\n width: 18px;\n height: 18px;\n}\n\n.heading {\n display: grid;\n min-width: 0;\n gap: 5px;\n flex: 1;\n}\n\n.brand {\n overflow: hidden;\n color: var(--ersc-text-secondary);\n font:\n 600 10px/1.1 ui-monospace,\n SFMono-Regular,\n Consolas,\n 'Liberation Mono',\n monospace;\n letter-spacing: 0.08em;\n text-overflow: ellipsis;\n text-transform: uppercase;\n white-space: nowrap;\n}\n\n.brand span {\n color: var(--ersc-text-tertiary);\n}\n\n.title {\n margin: 0;\n color: var(--ersc-text);\n font-size: 17px;\n font-weight: 600;\n line-height: 1.2;\n letter-spacing: -0.015em;\n}\n\n.failure-count {\n padding: 5px 8px;\n color: var(--ersc-text-secondary);\n font:\n 500 11px/1 ui-monospace,\n SFMono-Regular,\n Consolas,\n 'Liberation Mono',\n monospace;\n background: var(--ersc-raised);\n border: 1px solid var(--ersc-border);\n border-radius: 999px;\n white-space: nowrap;\n}\n\n.close {\n display: grid;\n width: 34px;\n height: 34px;\n padding: 0;\n color: var(--ersc-text-secondary);\n cursor: pointer;\n background: transparent;\n border: 1px solid transparent;\n border-radius: 7px;\n transition:\n color 140ms ease,\n background-color 140ms ease,\n border-color 140ms ease,\n transform 140ms var(--ersc-ease-out);\n place-items: center;\n}\n\n.close svg {\n width: 18px;\n height: 18px;\n}\n\n.close:focus-visible {\n color: var(--ersc-text);\n outline: 2px solid var(--ersc-danger);\n outline-offset: 2px;\n}\n\n.close:active {\n transform: scale(0.94);\n}\n\n.content {\n position: relative;\n min-height: 0;\n overflow: hidden;\n overscroll-behavior: contain;\n}\n\n.content-viewport {\n max-height: calc(100svh - clamp(124px, calc(12vh + 76px), 220px));\n overscroll-behavior: contain;\n scrollbar-width: none;\n}\n\n.scrollbar {\n position: absolute;\n top: 4px;\n right: 3px;\n bottom: 4px;\n display: flex;\n width: 8px;\n padding: 2px;\n border-radius: 999px;\n opacity: 0;\n transition: opacity 140ms ease;\n user-select: none;\n}\n\n.content[data-scrolling] .scrollbar,\n.scrollbar[data-hovering] {\n opacity: 1;\n}\n\n.scrollbar-thumb {\n width: 100%;\n background: #55514c;\n border-radius: inherit;\n}\n\narticle + article {\n border-top: 1px solid var(--ersc-border-subtle);\n}\n\n.failure {\n display: grid;\n grid-template-columns: 58px minmax(0, 1fr);\n}\n\n.failure-index {\n padding: 23px 12px;\n color: var(--ersc-text-tertiary);\n font:\n 500 11px/1 ui-monospace,\n SFMono-Regular,\n Consolas,\n 'Liberation Mono',\n monospace;\n text-align: center;\n background: var(--ersc-surface);\n border-right: 1px solid var(--ersc-border-subtle);\n}\n\n.failure-body {\n min-width: 0;\n padding: 22px 24px 24px;\n}\n\n.category,\n.component-stack-title,\n.message {\n margin: 0;\n}\n\n.category {\n display: flex;\n gap: 8px;\n align-items: center;\n color: var(--ersc-danger);\n font-size: 11px;\n font-weight: 650;\n line-height: 16px;\n letter-spacing: 0.08em;\n text-transform: uppercase;\n}\n\n.status-dot {\n width: 6px;\n height: 6px;\n background: currentcolor;\n border-radius: 50%;\n box-shadow: 0 0 0 3px rgb(255 97 90 / 12%);\n}\n\n.component-stack-title {\n margin: 0 0 8px;\n color: var(--ersc-text-secondary);\n font-size: 10px;\n font-weight: 600;\n line-height: 16px;\n letter-spacing: 0.08em;\n text-transform: uppercase;\n}\n\n.trace-section {\n margin-top: 18px;\n}\n\n.stack {\n margin-top: 16px;\n}\n\n.stack-trigger {\n display: flex;\n width: fit-content;\n gap: 5px;\n align-items: center;\n padding: 5px 0;\n color: var(--ersc-text-secondary);\n font:\n 600 11px/1.4 ui-monospace,\n SFMono-Regular,\n Consolas,\n 'Liberation Mono',\n monospace;\n cursor: pointer;\n background: transparent;\n border: 0;\n transition:\n color 140ms ease,\n transform 140ms var(--ersc-ease-out);\n}\n\n.stack-trigger:focus-visible {\n color: var(--ersc-text);\n outline: 2px solid var(--ersc-danger);\n outline-offset: 2px;\n}\n\n.stack-trigger:active {\n transform: scale(0.97);\n}\n\n.stack-trigger svg {\n width: 14px;\n height: 14px;\n transition: transform 160ms var(--ersc-ease-out);\n}\n\n.stack-trigger[aria-expanded='true'] svg {\n transform: rotate(90deg);\n}\n\n.stack-panel pre {\n margin-top: 6px;\n}\n\n.message {\n margin-top: 10px;\n color: var(--ersc-text);\n font:\n 500 15px/1.55 ui-monospace,\n SFMono-Regular,\n Consolas,\n 'Liberation Mono',\n monospace;\n letter-spacing: -0.015em;\n overflow-wrap: anywhere;\n}\n\n.error-name {\n color: #ff8c87;\n font-weight: 650;\n}\n\npre {\n margin: 0;\n overflow: auto;\n color: #cbc8c2;\n font:\n 12px/1.65 ui-monospace,\n SFMono-Regular,\n Consolas,\n 'Liberation Mono',\n monospace;\n tab-size: 2;\n word-break: break-word;\n white-space: pre-wrap;\n}\n\n.code-frame,\n.build-output {\n padding: 14px 16px;\n background: var(--ersc-surface);\n border: 1px solid var(--ersc-border-subtle);\n border-radius: 7px;\n}\n\n.build-failure {\n padding: 22px 24px 24px;\n}\n\n.section-heading {\n display: flex;\n gap: 16px;\n align-items: baseline;\n justify-content: space-between;\n margin-bottom: 10px;\n color: var(--ersc-text-secondary);\n font:\n 600 10px/1.4 ui-monospace,\n SFMono-Regular,\n Consolas,\n 'Liberation Mono',\n monospace;\n letter-spacing: 0.08em;\n text-transform: uppercase;\n}\n\n.section-heading span:last-child {\n color: var(--ersc-text-tertiary);\n font-weight: 500;\n letter-spacing: 0;\n text-transform: none;\n}\n\n.build-output {\n color: var(--ersc-text);\n background: #0d0d0c;\n border-left: 2px solid var(--ersc-danger);\n}\n\n@media (hover: hover) and (pointer: fine) {\n .close:hover {\n color: var(--ersc-text);\n background: var(--ersc-raised);\n border-color: var(--ersc-border);\n }\n\n .stack-trigger:hover {\n color: var(--ersc-text);\n }\n}\n\n@media (max-width: 560px) {\n .viewport {\n padding: 12px;\n }\n\n .popup {\n max-height: calc(100svh - 24px);\n }\n\n .content-viewport {\n max-height: calc(100svh - 92px);\n }\n\n header {\n min-height: 68px;\n gap: 10px;\n padding-inline: 12px;\n }\n\n .error-mark,\n .close {\n width: 32px;\n height: 32px;\n }\n\n .failure-count {\n display: none;\n }\n\n .failure {\n grid-template-columns: 38px minmax(0, 1fr);\n }\n\n .failure-index {\n padding-inline: 8px;\n }\n\n .failure-body,\n .build-failure {\n padding: 18px 16px 20px;\n }\n\n .section-heading span:last-child {\n display: none;\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .backdrop,\n .popup,\n .close,\n .stack-trigger,\n .stack-trigger svg,\n .scrollbar {\n transition-duration: 0ms;\n }\n\n .popup[data-starting-style],\n .popup[data-ending-style] {\n transform: none;\n }\n}\n\n.sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n white-space: nowrap;\n border: 0;\n clip: rect(0, 0, 0, 0);\n}\n";
9586
9586
 
9587
9587
 
9588
9588
 
@@ -9607,6 +9607,11 @@ const appendRuntimeFailure = (failures, failure)=>{
9607
9607
  };
9608
9608
  const applyDevPanelEvent = (state, event)=>{
9609
9609
  switch(event._tag){
9610
+ case 'Warning':
9611
+ return state._tag === 'Inactive' ? {
9612
+ _tag: 'Visible',
9613
+ content: event
9614
+ } : state;
9610
9615
  case 'RenderFailed':
9611
9616
  if (state._tag !== 'Inactive' && state.content._tag === 'BuildFailed') {
9612
9617
  return state;
@@ -9657,6 +9662,16 @@ const applyDevPanelEvent = (state, event)=>{
9657
9662
  failures: appendRuntimeFailure(state.content.failures, event.failure)
9658
9663
  }
9659
9664
  };
9665
+ case 'Warning':
9666
+ return {
9667
+ _tag: 'Visible',
9668
+ content: {
9669
+ _tag: 'RuntimeFailed',
9670
+ failures: [
9671
+ event.failure
9672
+ ]
9673
+ }
9674
+ };
9660
9675
  }
9661
9676
  }
9662
9677
  }
@@ -9787,7 +9802,7 @@ function RuntimeFailure({ failure, index }) {
9787
9802
  });
9788
9803
  }
9789
9804
  function DevPanelContent({ state }) {
9790
- if (state._tag === 'Inactive') {
9805
+ if (state._tag === 'Inactive' || state.content._tag === 'Warning') {
9791
9806
  return null;
9792
9807
  }
9793
9808
  const isBuildFailure = state.content._tag === 'BuildFailed';
@@ -9883,6 +9898,44 @@ function DevPanelContent({ state }) {
9883
9898
  });
9884
9899
  }
9885
9900
  function DevPanel({ onOpenChange, portalContainer, state }) {
9901
+ if (state._tag !== 'Inactive' && state.content._tag === 'Warning') {
9902
+ return state._tag === 'Dismissed' ? null : /*#__PURE__*/ jsxs(Fragment, {
9903
+ children: [
9904
+ /*#__PURE__*/ jsx("style", {
9905
+ children: panelraw_namespaceObject
9906
+ }),
9907
+ /*#__PURE__*/ jsxs("aside", {
9908
+ "aria-label": "Development warning",
9909
+ "aria-live": "polite",
9910
+ className: "warning-notice",
9911
+ children: [
9912
+ /*#__PURE__*/ jsxs("div", {
9913
+ children: [
9914
+ /*#__PURE__*/ jsx("span", {
9915
+ className: "brand",
9916
+ children: "effective-rsc / development"
9917
+ }),
9918
+ /*#__PURE__*/ jsx("h2", {
9919
+ className: "title",
9920
+ children: "Client navigation unavailable"
9921
+ }),
9922
+ /*#__PURE__*/ jsx("p", {
9923
+ children: state.content.message
9924
+ })
9925
+ ]
9926
+ }),
9927
+ /*#__PURE__*/ jsx("button", {
9928
+ "aria-label": "Dismiss development warning",
9929
+ className: "close",
9930
+ onClick: ()=>onOpenChange(false),
9931
+ type: "button",
9932
+ children: "\xd7"
9933
+ })
9934
+ ]
9935
+ })
9936
+ ]
9937
+ });
9938
+ }
9886
9939
  return /*#__PURE__*/ jsx(DialogRoot_DialogRoot, {
9887
9940
  onOpenChange: onOpenChange,
9888
9941
  open: state._tag === 'Visible',