effective-rsc 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/LLMS.md +382 -0
- package/README.md +235 -0
- package/bin/ersc.js +3 -0
- package/dist/application/component.d.ts +15 -0
- package/dist/application/component.js +20 -0
- package/dist/application/component.js.map +1 -0
- package/dist/application/definition.d.ts +31 -0
- package/dist/application/definition.js +57 -0
- package/dist/application/definition.js.map +1 -0
- package/dist/application/ersc-identity.d.ts +25 -0
- package/dist/application/ersc-identity.js +23 -0
- package/dist/application/ersc-identity.js.map +1 -0
- package/dist/application/ersc.d.ts +28 -0
- package/dist/application/ersc.js +72 -0
- package/dist/application/ersc.js.map +1 -0
- package/dist/application/layout.d.ts +18 -0
- package/dist/application/layout.js +17 -0
- package/dist/application/layout.js.map +1 -0
- package/dist/application/loading.d.ts +17 -0
- package/dist/application/loading.js +14 -0
- package/dist/application/loading.js.map +1 -0
- package/dist/application/middleware.d.ts +36 -0
- package/dist/application/middleware.js +46 -0
- package/dist/application/middleware.js.map +1 -0
- package/dist/application/page.d.ts +58 -0
- package/dist/application/page.js +50 -0
- package/dist/application/page.js.map +1 -0
- package/dist/application/render-runtime.d.ts +9 -0
- package/dist/application/render-runtime.js +28 -0
- package/dist/application/render-runtime.js.map +1 -0
- package/dist/application/route-graph.d.ts +22 -0
- package/dist/application/route-graph.js +68 -0
- package/dist/application/route-graph.js.map +1 -0
- package/dist/application/route-path.d.ts +31 -0
- package/dist/application/route-path.js +67 -0
- package/dist/application/route-path.js.map +1 -0
- package/dist/application/routes.d.ts +66 -0
- package/dist/application/routes.js +159 -0
- package/dist/application/routes.js.map +1 -0
- package/dist/application/server-fn.d.ts +30 -0
- package/dist/application/server-fn.js +58 -0
- package/dist/application/server-fn.js.map +1 -0
- package/dist/build/build.d.ts +26 -0
- package/dist/build/build.js +59 -0
- package/dist/build/build.js.map +1 -0
- package/dist/build/compiled-server.d.ts +53 -0
- package/dist/build/compiled-server.js +77 -0
- package/dist/build/compiled-server.js.map +1 -0
- package/dist/build/contract.d.ts +23 -0
- package/dist/build/contract.js +41 -0
- package/dist/build/contract.js.map +1 -0
- package/dist/build/dev-channel.d.ts +20 -0
- package/dist/build/dev-channel.js +71 -0
- package/dist/build/dev-channel.js.map +1 -0
- package/dist/build/dev.d.ts +41 -0
- package/dist/build/dev.js +186 -0
- package/dist/build/dev.js.map +1 -0
- package/dist/build/rsc-entry.d.ts +4 -0
- package/dist/build/rsc-entry.js +17 -0
- package/dist/build/rsc-entry.js.map +1 -0
- package/dist/build/rspack-config.d.ts +17 -0
- package/dist/build/rspack-config.js +259 -0
- package/dist/build/rspack-config.js.map +1 -0
- package/dist/build/rspack.d.ts +45 -0
- package/dist/build/rspack.js +231 -0
- package/dist/build/rspack.js.map +1 -0
- package/dist/build/terminal.d.ts +9 -0
- package/dist/build/terminal.js +17 -0
- package/dist/build/terminal.js.map +1 -0
- package/dist/cli.d.ts +14 -0
- package/dist/cli.js +116 -0
- package/dist/cli.js.map +1 -0
- package/dist/client/application.d.ts +2 -0
- package/dist/client/application.js +73 -0
- package/dist/client/application.js.map +1 -0
- package/dist/client/browser-capabilities.d.ts +9 -0
- package/dist/client/browser-capabilities.js +20 -0
- package/dist/client/browser-capabilities.js.map +1 -0
- package/dist/client/browser-effect-runner.d.ts +13 -0
- package/dist/client/browser-effect-runner.js +13 -0
- package/dist/client/browser-effect-runner.js.map +1 -0
- package/dist/client/browser-renderer.d.ts +70 -0
- package/dist/client/browser-renderer.js +159 -0
- package/dist/client/browser-renderer.js.map +1 -0
- package/dist/client/browser-screen.d.ts +2 -0
- package/dist/client/browser-screen.js +215 -0
- package/dist/client/browser-screen.js.map +1 -0
- package/dist/client/call-server.d.ts +8 -0
- package/dist/client/call-server.js +151 -0
- package/dist/client/call-server.js.map +1 -0
- package/dist/client/client-router.d.ts +6 -0
- package/dist/client/client-router.js +588 -0
- package/dist/client/client-router.js.map +1 -0
- package/dist/client/entry.d.ts +1 -0
- package/dist/client/entry.js +12 -0
- package/dist/client/entry.js.map +1 -0
- package/dist/client/flight-client.d.ts +83 -0
- package/dist/client/flight-client.js +146 -0
- package/dist/client/flight-client.js.map +1 -0
- package/dist/client/initial-flight-stream.d.ts +18 -0
- package/dist/client/initial-flight-stream.js +60 -0
- package/dist/client/initial-flight-stream.js.map +1 -0
- package/dist/client/navigation-api.d.ts +34 -0
- package/dist/client/navigation-api.js +25 -0
- package/dist/client/navigation-api.js.map +1 -0
- package/dist/client/navigation-routing.d.ts +3 -0
- package/dist/client/navigation-routing.js +8 -0
- package/dist/client/navigation-routing.js.map +1 -0
- package/dist/client/react-dom-renderer.d.ts +20 -0
- package/dist/client/react-dom-renderer.js +101 -0
- package/dist/client/react-dom-renderer.js.map +1 -0
- package/dist/client/route-loader.d.ts +81 -0
- package/dist/client/route-loader.js +128 -0
- package/dist/client/route-loader.js.map +1 -0
- package/dist/client/route-refresh.d.ts +31 -0
- package/dist/client/route-refresh.js +132 -0
- package/dist/client/route-refresh.js.map +1 -0
- package/dist/client/route-tree.d.ts +7 -0
- package/dist/client/route-tree.js +34 -0
- package/dist/client/route-tree.js.map +1 -0
- package/dist/dev/channel.d.ts +18 -0
- package/dist/dev/channel.js +30 -0
- package/dist/dev/channel.js.map +1 -0
- package/dist/dev/client.d.ts +3 -0
- package/dist/dev/client.js +132 -0
- package/dist/dev/client.js.map +1 -0
- package/dist/dev/hmr-update.d.ts +20 -0
- package/dist/dev/hmr-update.js +28 -0
- package/dist/dev/hmr-update.js.map +1 -0
- package/dist/dev/panel.d.ts +41 -0
- package/dist/dev/panel.js +9946 -0
- package/dist/dev/panel.js.map +1 -0
- package/dist/dev/runtime-failure.d.ts +11 -0
- package/dist/dev/runtime-failure.js +32 -0
- package/dist/dev/runtime-failure.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/rsc/flight.d.ts +16 -0
- package/dist/rsc/flight.js +6 -0
- package/dist/rsc/flight.js.map +1 -0
- package/dist/rsc/render-route-tree.d.ts +11 -0
- package/dist/rsc/render-route-tree.js +52 -0
- package/dist/rsc/render-route-tree.js.map +1 -0
- package/dist/rsc/route-tree.d.ts +6 -0
- package/dist/rsc/route-tree.js +5 -0
- package/dist/rsc/route-tree.js.map +1 -0
- package/dist/server/application.d.ts +17 -0
- package/dist/server/application.js +178 -0
- package/dist/server/application.js.map +1 -0
- package/dist/server/flight-html-stream.d.ts +19 -0
- package/dist/server/flight-html-stream.js +193 -0
- package/dist/server/flight-html-stream.js.map +1 -0
- package/dist/server/flight-renderer.d.ts +31 -0
- package/dist/server/flight-renderer.js +47 -0
- package/dist/server/flight-renderer.js.map +1 -0
- package/dist/server/html-renderer.d.ts +28 -0
- package/dist/server/html-renderer.js +82 -0
- package/dist/server/html-renderer.js.map +1 -0
- package/dist/server/request-outcome.d.ts +8 -0
- package/dist/server/request-outcome.js +5 -0
- package/dist/server/request-outcome.js.map +1 -0
- package/dist/server/server-config.d.ts +17 -0
- package/dist/server/server-config.js +18 -0
- package/dist/server/server-config.js.map +1 -0
- package/dist/server/server-fn-outcome.d.ts +13 -0
- package/dist/server/server-fn-outcome.js +24 -0
- package/dist/server/server-fn-outcome.js.map +1 -0
- package/dist/server/server-fn-request.d.ts +24 -0
- package/dist/server/server-fn-request.js +160 -0
- package/dist/server/server-fn-request.js.map +1 -0
- package/dist/unsupported.d.ts +0 -0
- package/dist/unsupported.js +5 -0
- package/dist/unsupported.js.map +1 -0
- package/docs/01-getting-started/01_first-application.tsx +26 -0
- package/docs/01-getting-started/20_styling.tsx +6 -0
- package/docs/01-getting-started/index.md +27 -0
- package/docs/01-getting-started/styles.css +1 -0
- package/docs/02-guides/01-server-functions/10_ersc.ts +6 -0
- package/docs/02-guides/01-server-functions/20_follow-author.ts +15 -0
- package/docs/02-guides/01-server-functions/30_follow-author-button.tsx +19 -0
- package/docs/02-guides/01-server-functions/40_application.tsx +30 -0
- package/docs/02-guides/01-server-functions/index.md +19 -0
- package/docs/02-guides/02-services/10_catalog.ts +22 -0
- package/docs/02-guides/02-services/20_application.tsx +44 -0
- package/docs/02-guides/02-services/index.md +17 -0
- package/docs/02-guides/03-routing/10_ersc.ts +6 -0
- package/docs/02-guides/03-routing/10_layouts.tsx +36 -0
- package/docs/02-guides/03-routing/20_pages.tsx +23 -0
- package/docs/02-guides/03-routing/30_routes.tsx +17 -0
- package/docs/02-guides/03-routing/40_application.ts +7 -0
- package/docs/02-guides/03-routing/index.md +22 -0
- package/docs/02-guides/04-middleware/10_auth.ts +27 -0
- package/docs/02-guides/04-middleware/20_account-page.tsx +13 -0
- package/docs/02-guides/04-middleware/30_update-profile.ts +16 -0
- package/docs/02-guides/04-middleware/40_application.tsx +20 -0
- package/docs/02-guides/04-middleware/index.md +25 -0
- package/docs/02-guides/05-http/10_application-layer.tsx +60 -0
- package/docs/02-guides/05-http/index.md +23 -0
- package/docs/02-guides/index.md +11 -0
- package/docs/03-advanced/01-request-runtime-and-lifetimes/index.md +13 -0
- package/docs/03-advanced/02-client-navigation/index.md +61 -0
- package/docs/03-advanced/03-server-function-execution-and-refresh/index.md +17 -0
- package/docs/03-advanced/index.md +11 -0
- package/docs/04-api-reference/01-application/index.md +8 -0
- package/docs/04-api-reference/02-page/index.md +12 -0
- package/docs/04-api-reference/03-layout/index.md +4 -0
- package/docs/04-api-reference/04-loading/index.md +4 -0
- package/docs/04-api-reference/05-component/10_component.tsx +18 -0
- package/docs/04-api-reference/05-component/index.md +10 -0
- package/docs/04-api-reference/06-middleware/index.md +33 -0
- package/docs/04-api-reference/07-routes/index.md +20 -0
- package/docs/04-api-reference/08-server-fn/index.md +43 -0
- package/docs/04-api-reference/index.md +17 -0
- package/docs/environment.d.ts +1 -0
- package/docs/index.md +15 -0
- package/logo-dark.svg +6 -0
- package/logo.svg +6 -0
- package/package.json +87 -0
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { Context, Effect, Layer } from 'effect';
|
|
2
|
+
import type { RouteTreeModel } from '../rsc/route-tree.js';
|
|
3
|
+
export type BrowserRendererNavigation = {
|
|
4
|
+
readonly committed: Promise<void>;
|
|
5
|
+
readonly discard: () => Promise<void>;
|
|
6
|
+
readonly retired: Promise<void>;
|
|
7
|
+
};
|
|
8
|
+
type BrowserRenderNavigationState = {
|
|
9
|
+
readonly committed: PromiseWithResolvers<void>;
|
|
10
|
+
readonly retired: PromiseWithResolvers<void>;
|
|
11
|
+
readonly routeTree: RouteTreeModel;
|
|
12
|
+
};
|
|
13
|
+
type BrowserRenderOwner = {
|
|
14
|
+
readonly _tag: 'Stable';
|
|
15
|
+
} | {
|
|
16
|
+
readonly _tag: 'Navigation';
|
|
17
|
+
readonly navigation: BrowserRenderNavigationState;
|
|
18
|
+
};
|
|
19
|
+
export type BrowserRender = {
|
|
20
|
+
readonly _tag: 'Initial';
|
|
21
|
+
readonly routeTree: RouteTreeModel;
|
|
22
|
+
} | {
|
|
23
|
+
readonly _tag: 'Navigation';
|
|
24
|
+
readonly navigation: BrowserRenderNavigationState;
|
|
25
|
+
readonly routeTree: RouteTreeModel;
|
|
26
|
+
} | {
|
|
27
|
+
readonly _tag: 'Discard';
|
|
28
|
+
readonly navigation: BrowserRenderNavigationState;
|
|
29
|
+
readonly routeTree: RouteTreeModel;
|
|
30
|
+
readonly visible: BrowserRenderOwner;
|
|
31
|
+
} | {
|
|
32
|
+
readonly _tag: 'Refresh';
|
|
33
|
+
readonly committed: PromiseWithResolvers<void>;
|
|
34
|
+
readonly routeTree: RouteTreeModel;
|
|
35
|
+
};
|
|
36
|
+
declare const BrowserRenderer_base: Context.ServiceClass<BrowserRenderer, "ersc/client/BrowserRenderer", {
|
|
37
|
+
commit: (render: BrowserRender) => void;
|
|
38
|
+
initialize: (initialRouteTree: RouteTreeModel, publish: (render: BrowserRender) => void) => void;
|
|
39
|
+
navigate: (routeTree: RouteTreeModel) => {
|
|
40
|
+
committed: Promise<void>;
|
|
41
|
+
discard: () => Promise<void>;
|
|
42
|
+
retired: Promise<void>;
|
|
43
|
+
};
|
|
44
|
+
refresh: (routeTree: RouteTreeModel) => Promise<void>;
|
|
45
|
+
}> & {
|
|
46
|
+
readonly make: Effect.Effect<{
|
|
47
|
+
commit: (render: BrowserRender) => void;
|
|
48
|
+
initialize: (initialRouteTree: RouteTreeModel, publish: (render: BrowserRender) => void) => void;
|
|
49
|
+
navigate: (routeTree: RouteTreeModel) => {
|
|
50
|
+
committed: Promise<void>;
|
|
51
|
+
discard: () => Promise<void>;
|
|
52
|
+
retired: Promise<void>;
|
|
53
|
+
};
|
|
54
|
+
refresh: (routeTree: RouteTreeModel) => Promise<void>;
|
|
55
|
+
}, never, never>;
|
|
56
|
+
};
|
|
57
|
+
export declare class BrowserRenderer extends BrowserRenderer_base {
|
|
58
|
+
static readonly layer: Layer.Layer<BrowserRenderer, never, never>;
|
|
59
|
+
static readonly layerTest: (implementation: {
|
|
60
|
+
commit: (render: BrowserRender) => void;
|
|
61
|
+
initialize: (initialRouteTree: RouteTreeModel, publish: (render: BrowserRender) => void) => void;
|
|
62
|
+
navigate: (routeTree: RouteTreeModel) => {
|
|
63
|
+
committed: Promise<void>;
|
|
64
|
+
discard: () => Promise<void>;
|
|
65
|
+
retired: Promise<void>;
|
|
66
|
+
};
|
|
67
|
+
refresh: (routeTree: RouteTreeModel) => Promise<void>;
|
|
68
|
+
}) => Layer.Layer<BrowserRenderer, never, never>;
|
|
69
|
+
}
|
|
70
|
+
export {};
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { Context, Effect, Layer, MutableRef } from "effect";
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
const getNavigationPhase = (lifecycle, navigation)=>{
|
|
6
|
+
const phase = lifecycle.phases.get(navigation);
|
|
7
|
+
if (phase === undefined) {
|
|
8
|
+
throw new TypeError('Browser navigation does not belong to this root.');
|
|
9
|
+
}
|
|
10
|
+
return phase;
|
|
11
|
+
};
|
|
12
|
+
const retireNavigation = (lifecycle, navigation)=>{
|
|
13
|
+
if (getNavigationPhase(lifecycle, navigation) !== 'Retired') {
|
|
14
|
+
lifecycle.phases.set(navigation, 'Retired');
|
|
15
|
+
navigation.retired.resolve();
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
class BrowserRenderer extends Context.Service()('ersc/client/BrowserRenderer', {
|
|
19
|
+
make: Effect.sync(()=>{
|
|
20
|
+
const state = MutableRef.make({
|
|
21
|
+
_tag: 'Uninitialized'
|
|
22
|
+
});
|
|
23
|
+
const getReadyState = ()=>{
|
|
24
|
+
const current = MutableRef.get(state);
|
|
25
|
+
if (current._tag === 'Uninitialized') {
|
|
26
|
+
throw new TypeError('BrowserRenderer must be initialized by ReactDOMRenderer.');
|
|
27
|
+
}
|
|
28
|
+
return current;
|
|
29
|
+
};
|
|
30
|
+
const initialize = (initialRouteTree, publish)=>{
|
|
31
|
+
const current = MutableRef.get(state);
|
|
32
|
+
if (current._tag === 'Ready') {
|
|
33
|
+
if (current.publish === publish) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
throw new TypeError('BrowserRenderer cannot be initialized by more than one React root.');
|
|
37
|
+
}
|
|
38
|
+
MutableRef.set(state, {
|
|
39
|
+
_tag: 'Ready',
|
|
40
|
+
lifecycle: {
|
|
41
|
+
active: {
|
|
42
|
+
_tag: 'Stable'
|
|
43
|
+
},
|
|
44
|
+
phases: new WeakMap(),
|
|
45
|
+
stableRouteTree: initialRouteTree,
|
|
46
|
+
visible: {
|
|
47
|
+
_tag: 'Stable'
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
publish
|
|
51
|
+
});
|
|
52
|
+
};
|
|
53
|
+
const navigate = (routeTree)=>{
|
|
54
|
+
const { lifecycle, publish } = getReadyState();
|
|
55
|
+
const navigation = {
|
|
56
|
+
committed: Promise.withResolvers(),
|
|
57
|
+
retired: Promise.withResolvers(),
|
|
58
|
+
routeTree
|
|
59
|
+
};
|
|
60
|
+
lifecycle.phases.set(navigation, 'Scheduled');
|
|
61
|
+
lifecycle.active = {
|
|
62
|
+
_tag: 'Navigation',
|
|
63
|
+
navigation
|
|
64
|
+
};
|
|
65
|
+
publish({
|
|
66
|
+
_tag: 'Navigation',
|
|
67
|
+
navigation,
|
|
68
|
+
routeTree: navigation.routeTree
|
|
69
|
+
});
|
|
70
|
+
return {
|
|
71
|
+
committed: navigation.committed.promise,
|
|
72
|
+
discard: ()=>{
|
|
73
|
+
if (getNavigationPhase(lifecycle, navigation) !== 'Scheduled') {
|
|
74
|
+
throw new TypeError('Only a scheduled browser navigation can be discarded.');
|
|
75
|
+
}
|
|
76
|
+
lifecycle.phases.set(navigation, 'DiscardRequested');
|
|
77
|
+
if (lifecycle.active._tag === 'Navigation' && lifecycle.active.navigation === navigation) {
|
|
78
|
+
const visible = lifecycle.visible;
|
|
79
|
+
lifecycle.active = visible._tag === 'Navigation' && getNavigationPhase(lifecycle, visible.navigation) === 'Visible' ? visible : {
|
|
80
|
+
_tag: 'Stable'
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
publish({
|
|
84
|
+
_tag: 'Discard',
|
|
85
|
+
navigation,
|
|
86
|
+
routeTree: lifecycle.visible._tag === 'Navigation' ? lifecycle.visible.navigation.routeTree : lifecycle.stableRouteTree,
|
|
87
|
+
visible: lifecycle.visible
|
|
88
|
+
});
|
|
89
|
+
return navigation.retired.promise;
|
|
90
|
+
},
|
|
91
|
+
retired: navigation.retired.promise
|
|
92
|
+
};
|
|
93
|
+
};
|
|
94
|
+
const refresh = (routeTree)=>{
|
|
95
|
+
const { lifecycle, publish } = getReadyState();
|
|
96
|
+
const committed = Promise.withResolvers();
|
|
97
|
+
lifecycle.active = {
|
|
98
|
+
_tag: 'Stable'
|
|
99
|
+
};
|
|
100
|
+
publish({
|
|
101
|
+
_tag: 'Refresh',
|
|
102
|
+
committed,
|
|
103
|
+
routeTree
|
|
104
|
+
});
|
|
105
|
+
return committed.promise;
|
|
106
|
+
};
|
|
107
|
+
const commit = (render)=>{
|
|
108
|
+
const { lifecycle } = getReadyState();
|
|
109
|
+
const previousVisible = lifecycle.visible;
|
|
110
|
+
const visible = render._tag === 'Navigation' ? {
|
|
111
|
+
_tag: 'Navigation',
|
|
112
|
+
navigation: render.navigation
|
|
113
|
+
} : render._tag === 'Discard' ? render.visible : {
|
|
114
|
+
_tag: 'Stable'
|
|
115
|
+
};
|
|
116
|
+
lifecycle.visible = visible;
|
|
117
|
+
if (previousVisible._tag === 'Navigation' && (visible._tag !== 'Navigation' || visible.navigation !== previousVisible.navigation)) {
|
|
118
|
+
retireNavigation(lifecycle, previousVisible.navigation);
|
|
119
|
+
}
|
|
120
|
+
switch(render._tag){
|
|
121
|
+
case 'Initial':
|
|
122
|
+
break;
|
|
123
|
+
case 'Discard':
|
|
124
|
+
retireNavigation(lifecycle, render.navigation);
|
|
125
|
+
break;
|
|
126
|
+
case 'Navigation':
|
|
127
|
+
if (getNavigationPhase(lifecycle, render.navigation) === 'Scheduled') {
|
|
128
|
+
lifecycle.phases.set(render.navigation, 'Visible');
|
|
129
|
+
}
|
|
130
|
+
if (lifecycle.active._tag === 'Navigation' && lifecycle.active.navigation === render.navigation) {
|
|
131
|
+
lifecycle.active = {
|
|
132
|
+
_tag: 'Stable'
|
|
133
|
+
};
|
|
134
|
+
lifecycle.stableRouteTree = render.routeTree;
|
|
135
|
+
lifecycle.phases.set(render.navigation, 'Completed');
|
|
136
|
+
}
|
|
137
|
+
render.navigation.committed.resolve();
|
|
138
|
+
break;
|
|
139
|
+
case 'Refresh':
|
|
140
|
+
lifecycle.stableRouteTree = render.routeTree;
|
|
141
|
+
render.committed.resolve();
|
|
142
|
+
break;
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
return {
|
|
146
|
+
commit,
|
|
147
|
+
initialize,
|
|
148
|
+
navigate,
|
|
149
|
+
refresh
|
|
150
|
+
};
|
|
151
|
+
})
|
|
152
|
+
}) {
|
|
153
|
+
static layer = Layer.effect(this, this.make);
|
|
154
|
+
static layerTest = Layer.mock(this);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export { BrowserRenderer };
|
|
158
|
+
|
|
159
|
+
//# sourceMappingURL=browser-renderer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client/browser-renderer.js","sources":["../../src/client/browser-renderer.ts"],"sourcesContent":["import { Context, Effect, Layer, MutableRef } from 'effect';\n\nimport type { RouteTreeModel } from '../rsc/route-tree';\n\nexport type BrowserRendererNavigation = {\n readonly committed: Promise<void>;\n readonly discard: () => Promise<void>;\n readonly retired: Promise<void>;\n};\n\ntype BrowserRenderNavigationState = {\n readonly committed: PromiseWithResolvers<void>;\n readonly retired: PromiseWithResolvers<void>;\n readonly routeTree: RouteTreeModel;\n};\n\ntype BrowserRenderNavigationPhase =\n | 'Scheduled'\n | 'DiscardRequested'\n | 'Visible'\n | 'Completed'\n | 'Retired';\n\ntype BrowserRenderOwner =\n | { readonly _tag: 'Stable' }\n | { readonly _tag: 'Navigation'; readonly navigation: BrowserRenderNavigationState };\n\ntype BrowserRendererLifecycle = {\n active: BrowserRenderOwner;\n readonly phases: WeakMap<BrowserRenderNavigationState, BrowserRenderNavigationPhase>;\n stableRouteTree: RouteTreeModel;\n visible: BrowserRenderOwner;\n};\n\ntype BrowserRendererState =\n | { readonly _tag: 'Uninitialized' }\n | {\n readonly _tag: 'Ready';\n readonly lifecycle: BrowserRendererLifecycle;\n readonly publish: (render: BrowserRender) => void;\n };\n\nexport type BrowserRender =\n | { readonly _tag: 'Initial'; readonly routeTree: RouteTreeModel }\n | {\n readonly _tag: 'Navigation';\n readonly navigation: BrowserRenderNavigationState;\n readonly routeTree: RouteTreeModel;\n }\n | {\n readonly _tag: 'Discard';\n readonly navigation: BrowserRenderNavigationState;\n readonly routeTree: RouteTreeModel;\n readonly visible: BrowserRenderOwner;\n }\n | {\n readonly _tag: 'Refresh';\n readonly committed: PromiseWithResolvers<void>;\n readonly routeTree: RouteTreeModel;\n };\n\nconst getNavigationPhase = (\n lifecycle: BrowserRendererLifecycle,\n navigation: BrowserRenderNavigationState,\n) => {\n const phase = lifecycle.phases.get(navigation);\n if (phase === undefined) {\n throw new TypeError('Browser navigation does not belong to this root.');\n }\n return phase;\n};\n\nconst retireNavigation = (\n lifecycle: BrowserRendererLifecycle,\n navigation: BrowserRenderNavigationState,\n) => {\n if (getNavigationPhase(lifecycle, navigation) !== 'Retired') {\n lifecycle.phases.set(navigation, 'Retired');\n navigation.retired.resolve();\n }\n};\n\nexport class BrowserRenderer extends Context.Service<BrowserRenderer>()(\n 'ersc/client/BrowserRenderer',\n {\n make: Effect.sync(() => {\n const state = MutableRef.make<BrowserRendererState>({ _tag: 'Uninitialized' });\n const getReadyState = () => {\n const current = MutableRef.get(state);\n if (current._tag === 'Uninitialized') {\n throw new TypeError('BrowserRenderer must be initialized by ReactDOMRenderer.');\n }\n return current;\n };\n\n const initialize = (\n initialRouteTree: RouteTreeModel,\n publish: (render: BrowserRender) => void,\n ) => {\n const current = MutableRef.get(state);\n if (current._tag === 'Ready') {\n if (current.publish === publish) {\n return;\n }\n throw new TypeError('BrowserRenderer cannot be initialized by more than one React root.');\n }\n\n MutableRef.set(state, {\n _tag: 'Ready',\n lifecycle: {\n active: { _tag: 'Stable' },\n phases: new WeakMap<BrowserRenderNavigationState, BrowserRenderNavigationPhase>(),\n stableRouteTree: initialRouteTree,\n visible: { _tag: 'Stable' },\n },\n publish,\n });\n };\n\n const navigate = (routeTree: RouteTreeModel) => {\n const { lifecycle, publish } = getReadyState();\n const navigation: BrowserRenderNavigationState = {\n committed: Promise.withResolvers<void>(),\n retired: Promise.withResolvers<void>(),\n routeTree,\n };\n lifecycle.phases.set(navigation, 'Scheduled');\n lifecycle.active = { _tag: 'Navigation', navigation };\n publish({ _tag: 'Navigation', navigation, routeTree: navigation.routeTree });\n\n return {\n committed: navigation.committed.promise,\n discard: () => {\n if (getNavigationPhase(lifecycle, navigation) !== 'Scheduled') {\n throw new TypeError('Only a scheduled browser navigation can be discarded.');\n }\n lifecycle.phases.set(navigation, 'DiscardRequested');\n if (\n lifecycle.active._tag === 'Navigation' &&\n lifecycle.active.navigation === navigation\n ) {\n const visible = lifecycle.visible;\n lifecycle.active =\n visible._tag === 'Navigation' &&\n getNavigationPhase(lifecycle, visible.navigation) === 'Visible'\n ? visible\n : { _tag: 'Stable' };\n }\n publish({\n _tag: 'Discard',\n navigation,\n routeTree:\n lifecycle.visible._tag === 'Navigation'\n ? lifecycle.visible.navigation.routeTree\n : lifecycle.stableRouteTree,\n visible: lifecycle.visible,\n });\n return navigation.retired.promise;\n },\n retired: navigation.retired.promise,\n };\n };\n\n const refresh = (routeTree: RouteTreeModel) => {\n const { lifecycle, publish } = getReadyState();\n const committed = Promise.withResolvers<void>();\n lifecycle.active = { _tag: 'Stable' };\n publish({ _tag: 'Refresh', committed, routeTree });\n return committed.promise;\n };\n\n const commit = (render: BrowserRender) => {\n const { lifecycle } = getReadyState();\n const previousVisible = lifecycle.visible;\n const visible: BrowserRenderOwner =\n render._tag === 'Navigation'\n ? { _tag: 'Navigation', navigation: render.navigation }\n : render._tag === 'Discard'\n ? render.visible\n : { _tag: 'Stable' };\n lifecycle.visible = visible;\n\n if (\n previousVisible._tag === 'Navigation' &&\n (visible._tag !== 'Navigation' || visible.navigation !== previousVisible.navigation)\n ) {\n retireNavigation(lifecycle, previousVisible.navigation);\n }\n\n switch (render._tag) {\n case 'Initial':\n break;\n case 'Discard':\n retireNavigation(lifecycle, render.navigation);\n break;\n case 'Navigation':\n if (getNavigationPhase(lifecycle, render.navigation) === 'Scheduled') {\n lifecycle.phases.set(render.navigation, 'Visible');\n }\n if (\n lifecycle.active._tag === 'Navigation' &&\n lifecycle.active.navigation === render.navigation\n ) {\n lifecycle.active = { _tag: 'Stable' };\n lifecycle.stableRouteTree = render.routeTree;\n lifecycle.phases.set(render.navigation, 'Completed');\n }\n render.navigation.committed.resolve();\n break;\n case 'Refresh':\n lifecycle.stableRouteTree = render.routeTree;\n render.committed.resolve();\n break;\n }\n };\n\n return { commit, initialize, navigate, refresh };\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","MutableRef","getNavigationPhase","lifecycle","navigation","phase","undefined","TypeError","retireNavigation","BrowserRenderer","state","getReadyState","current","initialize","initialRouteTree","publish","WeakMap","navigate","routeTree","Promise","visible","refresh","committed","commit","render","previousVisible"],"mappings":"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"}
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { createRoot } from "react-dom/client";
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
const BrowserFailureStyles = `
|
|
9
|
+
.ersc-browser-failure,
|
|
10
|
+
.ersc-browser-failure * {
|
|
11
|
+
box-sizing: border-box;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.ersc-browser-failure {
|
|
15
|
+
position: fixed;
|
|
16
|
+
inset: 0;
|
|
17
|
+
z-index: 2147483645;
|
|
18
|
+
display: grid;
|
|
19
|
+
min-width: 280px;
|
|
20
|
+
min-height: 100svh;
|
|
21
|
+
padding: 32px;
|
|
22
|
+
overflow: auto;
|
|
23
|
+
color: #20201e;
|
|
24
|
+
color-scheme: light;
|
|
25
|
+
background: #f5f4f0;
|
|
26
|
+
font-family: Inter, ui-sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
27
|
+
place-items: center;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.ersc-browser-failure__content {
|
|
31
|
+
width: min(100%, 520px);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.ersc-browser-failure__icon {
|
|
35
|
+
display: grid;
|
|
36
|
+
width: 44px;
|
|
37
|
+
height: 44px;
|
|
38
|
+
margin-bottom: 24px;
|
|
39
|
+
color: #c9342e;
|
|
40
|
+
background: #eee0dc;
|
|
41
|
+
border: 1px solid #e3c7c2;
|
|
42
|
+
border-radius: 9px;
|
|
43
|
+
place-items: center;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.ersc-browser-failure__icon svg {
|
|
47
|
+
width: 22px;
|
|
48
|
+
height: 22px;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.ersc-browser-failure h1 {
|
|
52
|
+
max-width: 460px;
|
|
53
|
+
margin: 0;
|
|
54
|
+
color: #20201e;
|
|
55
|
+
font-size: clamp(32px, 6vw, 44px);
|
|
56
|
+
font-weight: 650;
|
|
57
|
+
line-height: 1.05;
|
|
58
|
+
letter-spacing: -.045em;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.ersc-browser-failure__message {
|
|
62
|
+
max-width: 440px;
|
|
63
|
+
margin: 18px 0 0;
|
|
64
|
+
color: #696761;
|
|
65
|
+
font-size: 15px;
|
|
66
|
+
line-height: 1.65;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.ersc-browser-failure__rule {
|
|
70
|
+
height: 1px;
|
|
71
|
+
margin: 34px 0 20px;
|
|
72
|
+
background: #dcdad3;
|
|
73
|
+
border: 0;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.ersc-browser-failure__reload {
|
|
77
|
+
display: inline-flex;
|
|
78
|
+
min-height: 40px;
|
|
79
|
+
gap: 9px;
|
|
80
|
+
align-items: center;
|
|
81
|
+
justify-content: center;
|
|
82
|
+
padding: 0 15px;
|
|
83
|
+
color: #f8f8f6;
|
|
84
|
+
cursor: pointer;
|
|
85
|
+
background: #242422;
|
|
86
|
+
border: 1px solid #242422;
|
|
87
|
+
border-radius: 7px;
|
|
88
|
+
box-shadow: 0 1px 1px rgb(0 0 0 / 12%);
|
|
89
|
+
font: 600 13px/1 ui-sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
90
|
+
transition:
|
|
91
|
+
background-color 140ms ease,
|
|
92
|
+
transform 140ms cubic-bezier(.23, 1, .32, 1);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
.ersc-browser-failure__reload svg {
|
|
96
|
+
width: 15px;
|
|
97
|
+
height: 15px;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.ersc-browser-failure__reload:focus-visible {
|
|
101
|
+
outline: 2px solid #c9342e;
|
|
102
|
+
outline-offset: 3px;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.ersc-browser-failure__reload:active {
|
|
106
|
+
transform: scale(.97);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
@media (hover: hover) and (pointer: fine) {
|
|
110
|
+
.ersc-browser-failure__reload:hover {
|
|
111
|
+
background: #3a3936;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
@media (max-width: 480px) {
|
|
116
|
+
.ersc-browser-failure {
|
|
117
|
+
padding: 24px;
|
|
118
|
+
place-items: start center;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.ersc-browser-failure__content {
|
|
122
|
+
margin-top: max(32px, 10vh);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
@media (prefers-reduced-motion: reduce) {
|
|
127
|
+
.ersc-browser-failure__reload {
|
|
128
|
+
transition-duration: 0ms;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
`;
|
|
132
|
+
function BrowserFailureScreen() {
|
|
133
|
+
return /*#__PURE__*/ jsxs("main", {
|
|
134
|
+
"aria-labelledby": "ersc-browser-failure-title",
|
|
135
|
+
className: "ersc-browser-failure",
|
|
136
|
+
children: [
|
|
137
|
+
/*#__PURE__*/ jsx("style", {
|
|
138
|
+
children: BrowserFailureStyles
|
|
139
|
+
}),
|
|
140
|
+
/*#__PURE__*/ jsxs("div", {
|
|
141
|
+
className: "ersc-browser-failure__content",
|
|
142
|
+
children: [
|
|
143
|
+
/*#__PURE__*/ jsx("div", {
|
|
144
|
+
"aria-hidden": "true",
|
|
145
|
+
className: "ersc-browser-failure__icon",
|
|
146
|
+
children: /*#__PURE__*/ jsxs("svg", {
|
|
147
|
+
fill: "none",
|
|
148
|
+
viewBox: "0 0 22 22",
|
|
149
|
+
children: [
|
|
150
|
+
/*#__PURE__*/ jsx("path", {
|
|
151
|
+
d: "M11 6.25v5.5",
|
|
152
|
+
stroke: "currentColor",
|
|
153
|
+
strokeLinecap: "round",
|
|
154
|
+
strokeWidth: "1.7"
|
|
155
|
+
}),
|
|
156
|
+
/*#__PURE__*/ jsx("circle", {
|
|
157
|
+
cx: "11",
|
|
158
|
+
cy: "15.25",
|
|
159
|
+
fill: "currentColor",
|
|
160
|
+
r: ".9"
|
|
161
|
+
}),
|
|
162
|
+
/*#__PURE__*/ jsx("path", {
|
|
163
|
+
d: "M8.93 3.33 2.18 15a2.4 2.4 0 0 0 2.08 3.6h13.48a2.4 2.4 0 0 0 2.08-3.6L13.07 3.33a2.4 2.4 0 0 0-4.14 0Z",
|
|
164
|
+
stroke: "currentColor",
|
|
165
|
+
strokeWidth: "1.7"
|
|
166
|
+
})
|
|
167
|
+
]
|
|
168
|
+
})
|
|
169
|
+
}),
|
|
170
|
+
/*#__PURE__*/ jsx("h1", {
|
|
171
|
+
id: "ersc-browser-failure-title",
|
|
172
|
+
children: "Something went wrong"
|
|
173
|
+
}),
|
|
174
|
+
/*#__PURE__*/ jsx("p", {
|
|
175
|
+
className: "ersc-browser-failure__message",
|
|
176
|
+
children: "This page couldn’t be displayed. Try reloading it."
|
|
177
|
+
}),
|
|
178
|
+
/*#__PURE__*/ jsx("hr", {
|
|
179
|
+
className: "ersc-browser-failure__rule"
|
|
180
|
+
}),
|
|
181
|
+
/*#__PURE__*/ jsxs("button", {
|
|
182
|
+
className: "ersc-browser-failure__reload",
|
|
183
|
+
type: "button",
|
|
184
|
+
onClick: ()=>window.location.reload(),
|
|
185
|
+
children: [
|
|
186
|
+
/*#__PURE__*/ jsx("svg", {
|
|
187
|
+
"aria-hidden": "true",
|
|
188
|
+
fill: "none",
|
|
189
|
+
viewBox: "0 0 16 16",
|
|
190
|
+
children: /*#__PURE__*/ jsx("path", {
|
|
191
|
+
d: "M13 5.5A5.5 5.5 0 1 0 13.15 10M13 2.75V5.5h-2.75",
|
|
192
|
+
stroke: "currentColor",
|
|
193
|
+
strokeLinecap: "round",
|
|
194
|
+
strokeLinejoin: "round",
|
|
195
|
+
strokeWidth: "1.4"
|
|
196
|
+
})
|
|
197
|
+
}),
|
|
198
|
+
"Reload page"
|
|
199
|
+
]
|
|
200
|
+
})
|
|
201
|
+
]
|
|
202
|
+
})
|
|
203
|
+
]
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
const renderBrowserScreen = (screen)=>{
|
|
207
|
+
const container = document.createElement('div');
|
|
208
|
+
document.body.replaceChildren(container);
|
|
209
|
+
createRoot(container).render(screen);
|
|
210
|
+
};
|
|
211
|
+
const showBrowserFailure = ()=>renderBrowserScreen(/*#__PURE__*/ jsx(BrowserFailureScreen, {}));
|
|
212
|
+
|
|
213
|
+
export { BrowserFailureScreen, showBrowserFailure };
|
|
214
|
+
|
|
215
|
+
//# sourceMappingURL=browser-screen.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client/browser-screen.js","sources":["../../src/client/browser-screen.tsx"],"sourcesContent":["import type { ReactNode } from 'react';\nimport { createRoot } from 'react-dom/client';\n\nconst BrowserFailureStyles = `\n .ersc-browser-failure,\n .ersc-browser-failure * {\n box-sizing: border-box;\n }\n\n .ersc-browser-failure {\n position: fixed;\n inset: 0;\n z-index: 2147483645;\n display: grid;\n min-width: 280px;\n min-height: 100svh;\n padding: 32px;\n overflow: auto;\n color: #20201e;\n color-scheme: light;\n background: #f5f4f0;\n font-family: Inter, ui-sans-serif, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n place-items: center;\n }\n\n .ersc-browser-failure__content {\n width: min(100%, 520px);\n }\n\n .ersc-browser-failure__icon {\n display: grid;\n width: 44px;\n height: 44px;\n margin-bottom: 24px;\n color: #c9342e;\n background: #eee0dc;\n border: 1px solid #e3c7c2;\n border-radius: 9px;\n place-items: center;\n }\n\n .ersc-browser-failure__icon svg {\n width: 22px;\n height: 22px;\n }\n\n .ersc-browser-failure h1 {\n max-width: 460px;\n margin: 0;\n color: #20201e;\n font-size: clamp(32px, 6vw, 44px);\n font-weight: 650;\n line-height: 1.05;\n letter-spacing: -.045em;\n }\n\n .ersc-browser-failure__message {\n max-width: 440px;\n margin: 18px 0 0;\n color: #696761;\n font-size: 15px;\n line-height: 1.65;\n }\n\n .ersc-browser-failure__rule {\n height: 1px;\n margin: 34px 0 20px;\n background: #dcdad3;\n border: 0;\n }\n\n .ersc-browser-failure__reload {\n display: inline-flex;\n min-height: 40px;\n gap: 9px;\n align-items: center;\n justify-content: center;\n padding: 0 15px;\n color: #f8f8f6;\n cursor: pointer;\n background: #242422;\n border: 1px solid #242422;\n border-radius: 7px;\n box-shadow: 0 1px 1px rgb(0 0 0 / 12%);\n font: 600 13px/1 ui-sans-serif, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n transition:\n background-color 140ms ease,\n transform 140ms cubic-bezier(.23, 1, .32, 1);\n }\n\n .ersc-browser-failure__reload svg {\n width: 15px;\n height: 15px;\n }\n\n .ersc-browser-failure__reload:focus-visible {\n outline: 2px solid #c9342e;\n outline-offset: 3px;\n }\n\n .ersc-browser-failure__reload:active {\n transform: scale(.97);\n }\n\n @media (hover: hover) and (pointer: fine) {\n .ersc-browser-failure__reload:hover {\n background: #3a3936;\n }\n }\n\n @media (max-width: 480px) {\n .ersc-browser-failure {\n padding: 24px;\n place-items: start center;\n }\n\n .ersc-browser-failure__content {\n margin-top: max(32px, 10vh);\n }\n }\n\n @media (prefers-reduced-motion: reduce) {\n .ersc-browser-failure__reload {\n transition-duration: 0ms;\n }\n }\n`;\n\nexport function BrowserFailureScreen() {\n return (\n <main aria-labelledby='ersc-browser-failure-title' className='ersc-browser-failure'>\n <style>{BrowserFailureStyles}</style>\n <div className='ersc-browser-failure__content'>\n <div aria-hidden='true' className='ersc-browser-failure__icon'>\n <svg fill='none' viewBox='0 0 22 22'>\n <path d='M11 6.25v5.5' stroke='currentColor' strokeLinecap='round' strokeWidth='1.7' />\n <circle cx='11' cy='15.25' fill='currentColor' r='.9' />\n <path\n d='M8.93 3.33 2.18 15a2.4 2.4 0 0 0 2.08 3.6h13.48a2.4 2.4 0 0 0 2.08-3.6L13.07 3.33a2.4 2.4 0 0 0-4.14 0Z'\n stroke='currentColor'\n strokeWidth='1.7'\n />\n </svg>\n </div>\n <h1 id='ersc-browser-failure-title'>Something went wrong</h1>\n <p className='ersc-browser-failure__message'>\n This page couldn’t be displayed. Try reloading it.\n </p>\n <hr className='ersc-browser-failure__rule' />\n <button\n className='ersc-browser-failure__reload'\n type='button'\n onClick={() => window.location.reload()}\n >\n <svg aria-hidden='true' fill='none' viewBox='0 0 16 16'>\n <path\n d='M13 5.5A5.5 5.5 0 1 0 13.15 10M13 2.75V5.5h-2.75'\n stroke='currentColor'\n strokeLinecap='round'\n strokeLinejoin='round'\n strokeWidth='1.4'\n />\n </svg>\n Reload page\n </button>\n </div>\n </main>\n );\n}\n\nconst renderBrowserScreen = (screen: ReactNode) => {\n const container = document.createElement('div');\n document.body.replaceChildren(container);\n createRoot(container).render(screen);\n};\n\nexport const showBrowserFailure = () => renderBrowserScreen(<BrowserFailureScreen />);\n"],"names":["createRoot","BrowserFailureStyles","BrowserFailureScreen","window","renderBrowserScreen","screen","container","document","showBrowserFailure"],"mappings":";;;;;;AAC8C;AAE9C,MAAMC,oBAAoBA,GAAG,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2H9B,CAAC;AAEM,SAASC,oBAAoBA;IAClC,qBACE,KAAC;QAAK,mBAAgB;QAA6B,WAAU;;0BAC3D,IAAC;0BAAOD,oBAAoBA;;0BAC5B,KAAC;gBAAI,WAAU;;kCACb,IAAC;wBAAI,eAAY;wBAAO,WAAU;kCAChC,mBAAC;4BAAI,MAAK;4BAAO,SAAQ;;8CACvB,IAAC;oCAAK,GAAE;oCAAe,QAAO;oCAAe,eAAc;oCAAQ,aAAY;;8CAC/E,IAAC;oCAAO,IAAG;oCAAK,IAAG;oCAAQ,MAAK;oCAAe,GAAE;;8CACjD,IAAC;oCACC,GAAE;oCACF,QAAO;oCACP,aAAY;;;;;kCAIlB,IAAC;wBAAG,IAAG;kCAA6B;;kCACpC,IAAC;wBAAE,WAAU;kCAAgC;;kCAG7C,IAAC;wBAAG,WAAU;;kCACd,KAAC;wBACC,WAAU;wBACV,MAAK;wBACL,SAAS,IAAME,OAAO,QAAQ,CAAC,MAAM;;0CAErC,IAAC;gCAAI,eAAY;gCAAO,MAAK;gCAAO,SAAQ;0CAC1C,kBAAC;oCACC,GAAE;oCACF,QAAO;oCACP,eAAc;oCACd,gBAAe;oCACf,aAAY;;;4BAEV;;;;;;;AAMhB;AAEA,MAAMC,mBAAmBA,GAAG,CAACC;IAC3B,MAAMC,YAAYC,SAAS,aAAa,CAAC;IACzCA,SAAS,IAAI,CAAC,eAAe,CAACD;IAC9BN,UAAUA,CAACM,WAAW,MAAM,CAACD;AAC/B;AAEO,MAAMG,kBAAkBA,GAAG,IAAMJ,mBAAmBA,eAAC,IAACF,oBAAoBA,OAAK"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Effect } from 'effect';
|
|
2
|
+
import { BrowserEffectRunner } from './browser-effect-runner.js';
|
|
3
|
+
import { BrowserRenderer } from './browser-renderer.js';
|
|
4
|
+
import { FlightClient } from './flight-client.js';
|
|
5
|
+
import { NavigationApi } from './navigation-api.js';
|
|
6
|
+
import { RouteLoader } from './route-loader.js';
|
|
7
|
+
import { RouteRefresher } from './route-refresh.js';
|
|
8
|
+
export declare const installCallServer: Effect.Effect<void, never, BrowserEffectRunner | BrowserRenderer | FlightClient | NavigationApi | RouteLoader | RouteRefresher>;
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { Effect, MutableRef, Schema } from "effect";
|
|
2
|
+
import { addTransitionType, startTransition } from "react";
|
|
3
|
+
import { createTemporaryReferenceSet, encodeReply, setServerCallback } from "react-server-dom-rspack/client.browser";
|
|
4
|
+
import { BrowserEffectRunner } from "./browser-effect-runner.js";
|
|
5
|
+
import { BrowserRenderer } from "./browser-renderer.js";
|
|
6
|
+
import { FlightClient } from "./flight-client.js";
|
|
7
|
+
import { NavigationApi } from "./navigation-api.js";
|
|
8
|
+
import { RouteLoader } from "./route-loader.js";
|
|
9
|
+
import { RouteRefresher } from "./route-refresh.js";
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class ServerFnCallError extends Schema.TaggedError()('ServerFnCallError', {
|
|
30
|
+
cause: Schema.Defect(),
|
|
31
|
+
message: Schema.String
|
|
32
|
+
}) {
|
|
33
|
+
}
|
|
34
|
+
const installCallServer = Effect.gen(function*() {
|
|
35
|
+
const browserRenderer = yield* BrowserRenderer;
|
|
36
|
+
const navigationApi = yield* NavigationApi;
|
|
37
|
+
const run = yield* BrowserEffectRunner;
|
|
38
|
+
const flightClient = yield* FlightClient;
|
|
39
|
+
const routeLoader = yield* RouteLoader;
|
|
40
|
+
const routeRefresher = yield* RouteRefresher;
|
|
41
|
+
const latestInvocationOrder = MutableRef.make(0);
|
|
42
|
+
const selectRefreshSource = (invocation)=>{
|
|
43
|
+
if (MutableRef.get(latestInvocationOrder) !== invocation.order || navigationApi.getTransition() !== null) {
|
|
44
|
+
return 'CurrentRoute';
|
|
45
|
+
}
|
|
46
|
+
const currentEntry = navigationApi.getCurrentEntry();
|
|
47
|
+
switch(invocation._tag){
|
|
48
|
+
case 'HistoryEntry':
|
|
49
|
+
return currentEntry?.id === invocation.id ? 'Response' : 'CurrentRoute';
|
|
50
|
+
case 'CurrentUrl':
|
|
51
|
+
return currentEntry === null && navigationApi.getCurrentUrl() === invocation.url ? 'Response' : 'CurrentRoute';
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
const callServer = Effect.fnUntraced(function*(id, args, invocationResult) {
|
|
55
|
+
const currentEntry = navigationApi.getCurrentEntry();
|
|
56
|
+
const order = MutableRef.incrementAndGet(latestInvocationOrder);
|
|
57
|
+
const invocation = currentEntry === null ? {
|
|
58
|
+
_tag: 'CurrentUrl',
|
|
59
|
+
order,
|
|
60
|
+
url: navigationApi.getCurrentUrl()
|
|
61
|
+
} : {
|
|
62
|
+
_tag: 'HistoryEntry',
|
|
63
|
+
id: currentEntry.id,
|
|
64
|
+
order,
|
|
65
|
+
url: currentEntry.url ?? navigationApi.getCurrentUrl()
|
|
66
|
+
};
|
|
67
|
+
const temporaryReferences = createTemporaryReferenceSet();
|
|
68
|
+
const body = yield* Effect.tryPromise({
|
|
69
|
+
try: ()=>encodeReply(args, {
|
|
70
|
+
temporaryReferences
|
|
71
|
+
}),
|
|
72
|
+
catch: (cause)=>new ServerFnCallError({
|
|
73
|
+
cause,
|
|
74
|
+
message: 'Failed to encode arguments.'
|
|
75
|
+
})
|
|
76
|
+
});
|
|
77
|
+
const resource = yield* flightClient.load({
|
|
78
|
+
_tag: 'ServerFunction',
|
|
79
|
+
body,
|
|
80
|
+
destination: new URL(invocation.url),
|
|
81
|
+
id,
|
|
82
|
+
temporaryReferences
|
|
83
|
+
}).pipe(Effect.mapError((cause)=>new ServerFnCallError({
|
|
84
|
+
cause,
|
|
85
|
+
message: 'Server Function request failed.'
|
|
86
|
+
})));
|
|
87
|
+
if (resource._tag === 'Document') {
|
|
88
|
+
yield* resource.release;
|
|
89
|
+
return yield* new ServerFnCallError({
|
|
90
|
+
cause: new Error('A Server Function response cannot request document navigation.'),
|
|
91
|
+
message: 'Server Function response was incompatible with Flight.'
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
const serverFnResult = resource.payload.serverFnResult;
|
|
95
|
+
if (serverFnResult === null) {
|
|
96
|
+
yield* resource.release;
|
|
97
|
+
return yield* new ServerFnCallError({
|
|
98
|
+
cause: new Error('The Flight payload omitted the Server Function return value.'),
|
|
99
|
+
message: 'Server Function response was incomplete.'
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
switch(serverFnResult._tag){
|
|
103
|
+
case 'Failure':
|
|
104
|
+
invocationResult.reject(new ServerFnCallError({
|
|
105
|
+
cause: serverFnResult.error,
|
|
106
|
+
message: 'Server Function execution failed.'
|
|
107
|
+
}));
|
|
108
|
+
break;
|
|
109
|
+
case 'Success':
|
|
110
|
+
invocationResult.resolve(serverFnResult.value);
|
|
111
|
+
break;
|
|
112
|
+
}
|
|
113
|
+
// React registered its Action reactions before the request completed. Registering ERSC's
|
|
114
|
+
// continuation after settlement lets React close that Action before the refresh Transition.
|
|
115
|
+
yield* Effect.promise(()=>invocationResult.promise.then(()=>undefined, ()=>undefined));
|
|
116
|
+
if (selectRefreshSource(invocation) === 'CurrentRoute') {
|
|
117
|
+
yield* resource.release;
|
|
118
|
+
yield* routeRefresher.refreshCurrentRoute('server-function');
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
yield* routeRefresher.interruptCurrentRouteRefresh;
|
|
122
|
+
const commitRefresh = routeLoader.prepareRefresh(resource.payload.routeTree);
|
|
123
|
+
let renderCommitted;
|
|
124
|
+
yield* Effect.sync(()=>{
|
|
125
|
+
startTransition(()=>{
|
|
126
|
+
addTransitionType('server-function');
|
|
127
|
+
// Do not return the commit Promise from React's Transition Action. React cannot commit the
|
|
128
|
+
// render until that Action ends.
|
|
129
|
+
renderCommitted = browserRenderer.refresh(resource.payload.routeTree);
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
yield* Effect.all([
|
|
133
|
+
resource.completed,
|
|
134
|
+
Effect.promise(()=>renderCommitted)
|
|
135
|
+
], {
|
|
136
|
+
concurrency: 'unbounded',
|
|
137
|
+
discard: true
|
|
138
|
+
}).pipe(Effect.andThen(Effect.sync(commitRefresh)), Effect.onError(()=>resource.release), Effect.forkScoped);
|
|
139
|
+
});
|
|
140
|
+
yield* Effect.sync(()=>{
|
|
141
|
+
setServerCallback((id, args)=>{
|
|
142
|
+
const invocationResult = Promise.withResolvers();
|
|
143
|
+
void run(callServer(id, args, invocationResult)).catch(invocationResult.reject);
|
|
144
|
+
return invocationResult.promise;
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
export { installCallServer };
|
|
150
|
+
|
|
151
|
+
//# sourceMappingURL=call-server.js.map
|