@lazarv/react-server 0.0.0-experimental-43e79e6-20230928

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +5 -0
  3. package/bin/cli.mjs +93 -0
  4. package/bin/commands/build.mjs +19 -0
  5. package/bin/commands/dev.mjs +23 -0
  6. package/bin/commands/start.mjs +16 -0
  7. package/bin/loader.mjs +38 -0
  8. package/client/ActionState.mjs +16 -0
  9. package/client/ClientOnly.jsx +14 -0
  10. package/client/ClientProvider.jsx +243 -0
  11. package/client/ErrorBoundary.jsx +45 -0
  12. package/client/FlightContext.mjs +3 -0
  13. package/client/Link.jsx +59 -0
  14. package/client/Params.mjs +15 -0
  15. package/client/ReactServerComponent.jsx +77 -0
  16. package/client/Refresh.jsx +52 -0
  17. package/client/components.mjs +28 -0
  18. package/client/context.mjs +6 -0
  19. package/client/entry.client.jsx +146 -0
  20. package/client/index.jsx +6 -0
  21. package/client/navigation.jsx +4 -0
  22. package/config/context.mjs +37 -0
  23. package/config/index.mjs +114 -0
  24. package/lib/build/action.mjs +57 -0
  25. package/lib/build/banner.mjs +13 -0
  26. package/lib/build/chunks.mjs +26 -0
  27. package/lib/build/client.mjs +114 -0
  28. package/lib/build/custom-logger.mjs +13 -0
  29. package/lib/build/dependencies.mjs +54 -0
  30. package/lib/build/resolve.mjs +101 -0
  31. package/lib/build/server.mjs +142 -0
  32. package/lib/build/static.mjs +89 -0
  33. package/lib/dev/action.mjs +63 -0
  34. package/lib/dev/create-logger.mjs +52 -0
  35. package/lib/dev/create-server.mjs +208 -0
  36. package/lib/dev/modules.mjs +20 -0
  37. package/lib/dev/ssr-handler.mjs +135 -0
  38. package/lib/handlers/error.mjs +153 -0
  39. package/lib/handlers/not-found.mjs +5 -0
  40. package/lib/handlers/redirect.mjs +1 -0
  41. package/lib/handlers/rewrite.mjs +1 -0
  42. package/lib/handlers/static.mjs +120 -0
  43. package/lib/handlers/trailing-slash.mjs +12 -0
  44. package/lib/plugins/react-server.mjs +73 -0
  45. package/lib/plugins/use-client.mjs +135 -0
  46. package/lib/plugins/use-server.mjs +175 -0
  47. package/lib/start/action.mjs +110 -0
  48. package/lib/start/create-server.mjs +111 -0
  49. package/lib/start/manifest.mjs +104 -0
  50. package/lib/start/ssr-handler.mjs +134 -0
  51. package/lib/sys.mjs +49 -0
  52. package/lib/utils/merge.mjs +31 -0
  53. package/lib/utils/server-address.mjs +14 -0
  54. package/memory-cache/index.mjs +125 -0
  55. package/package.json +81 -0
  56. package/react-server.d.ts +209 -0
  57. package/server/ErrorBoundary.jsx +14 -0
  58. package/server/RemoteComponent.jsx +210 -0
  59. package/server/Route.jsx +108 -0
  60. package/server/actions.mjs +72 -0
  61. package/server/cache.mjs +19 -0
  62. package/server/client-component.mjs +62 -0
  63. package/server/context.mjs +32 -0
  64. package/server/cookies.mjs +14 -0
  65. package/server/entry.server.jsx +972 -0
  66. package/server/error-boundary.jsx +2 -0
  67. package/server/http-headers.mjs +8 -0
  68. package/server/http-status.mjs +6 -0
  69. package/server/index.mjs +14 -0
  70. package/server/logger.mjs +15 -0
  71. package/server/module-loader.mjs +20 -0
  72. package/server/redirects.mjs +45 -0
  73. package/server/remote-component.jsx +2 -0
  74. package/server/request.mjs +37 -0
  75. package/server/revalidate.mjs +22 -0
  76. package/server/rewrites.mjs +0 -0
  77. package/server/router.jsx +6 -0
  78. package/server/runtime.mjs +32 -0
  79. package/server/symbols.mjs +24 -0
@@ -0,0 +1,209 @@
1
+ declare module "@lazarv/react-server" {
2
+ import type { RequestContextExtensions } from "@hattip/compose";
3
+ import type { CookieSerializeOptions } from "@hattip/cookie";
4
+
5
+ export function server$<T extends (...args: any[]) => any>(action: T): T;
6
+ export function cache$<T extends React.FC>(
7
+ Component: T,
8
+ ttl?: number | true
9
+ ): T;
10
+ export function redirect(url: string, status?: number): void;
11
+
12
+ export function useRequest(): Request;
13
+ export function useResponse(): Response;
14
+ export function useUrl(): URL;
15
+ export function useFormData(): FormData;
16
+ export function rewrite(pathname: string): void;
17
+
18
+ export function revalidate(key?: string): void;
19
+ export function status(status?: number, statusText?: string): void;
20
+ export function headers(headers?: Record<string, string>): void;
21
+
22
+ export type Cookies = RequestContextExtensions["cookie"];
23
+ export function cookie(): Cookies;
24
+ export function setCookie(
25
+ name: string,
26
+ value: string,
27
+ options?: CookieSerializeOptions
28
+ ): void;
29
+ export function deleteCookie(
30
+ name: string,
31
+ options?: CookieSerializeOptions
32
+ ): void;
33
+ }
34
+
35
+ declare module "@lazarv/react-server/client" {
36
+ export type ReactServerClientContext = {
37
+ registerOutlet(outlet: string, url: string): void;
38
+ refresh(outlet?: string): Promise<void>;
39
+ prefetch(
40
+ url: string,
41
+ options?: { outlet?: string; ttl?: number }
42
+ ): Promise<void>;
43
+ navigate(
44
+ url: string,
45
+ options?: { outlet?: string; push?: boolean; rollback?: number }
46
+ ): Promise<void>;
47
+ replace(
48
+ url: string,
49
+ options?: { outlet?: string; rollback?: number }
50
+ ): Promise<void>;
51
+ subscribe(
52
+ url: string,
53
+ listener: (
54
+ to: string,
55
+ done?: (err: unknown | null, result: unknown) => void
56
+ ) => void
57
+ ): () => void;
58
+ getFlightResponse(): Promise<Response | null>;
59
+ };
60
+ export function useClient(): ReactServerClientContext;
61
+ }
62
+
63
+ declare module "@lazarv/react-server/config" {
64
+ export type ReactServerConfig = any;
65
+ export function loadConfig<T extends Record<string, unknown>>(
66
+ initialConfig: T
67
+ ): Promise<ReactServerConfig>;
68
+ export function defineConfig<T extends Record<string, unknown>>(
69
+ config: T
70
+ ): ReactServerConfig;
71
+
72
+ export function forRoot(config?: ReactServerConfig): ReactServerConfig;
73
+ export function forChild(config?: ReactServerConfig): ReactServerConfig;
74
+ }
75
+
76
+ declare module "@lazarv/react-server/error-boundary" {
77
+ import type { ErrorBoundaryProps } from "react-error-boundary";
78
+
79
+ export type ReactServerErrorBoundaryProps = React.PropsWithChildren<
80
+ Omit<ErrorBoundaryProps, "fallback"> & { fallback: React.ReactNode }
81
+ >;
82
+ const ErrorBoundary: React.FC<ReactServerErrorBoundaryProps>;
83
+ export default ErrorBoundary;
84
+ }
85
+
86
+ declare module "@lazarv/react-server/memory-cache" {
87
+ export interface ReactServerCache {
88
+ get<T = unknown>(keys: string[]): Promise<T | undefined>;
89
+ set<T = unknown>(keys: string[], value: T): Promise<void>;
90
+ has(keys: string[]): Promise<boolean>;
91
+ setExpiry(keys: string[], ttl: number): Promise<void>;
92
+ hasExpiry(keys: string[], ttl: number): Promise<boolean>;
93
+ delete(keys: string[]): Promise<void>;
94
+ }
95
+
96
+ export class MemoryCache implements ReactServerCache {
97
+ get<T = unknown>(keys: string[]): Promise<T | undefined>;
98
+ set<T = unknown>(keys: string[], value: T): Promise<void>;
99
+ has(keys: string[]): Promise<boolean>;
100
+ setExpiry(keys: string[], ttl: number): Promise<void>;
101
+ hasExpiry(keys: string[], ttl: number): Promise<boolean>;
102
+ delete(keys: string[]): Promise<void>;
103
+ }
104
+
105
+ export function useCache<T>(
106
+ keys: string[],
107
+ value: (() => Promise<T>) | T,
108
+ ttl?: number,
109
+ force?: boolean
110
+ ): Promise<T>;
111
+ }
112
+
113
+ declare module "@lazarv/react-server/navigation" {
114
+ import type { ReactServerRouting } from "@lazarv/react-server/router";
115
+
116
+ export const Link: React.FC<
117
+ React.PropsWithChildren<{
118
+ to: ReactServerRouting["path"];
119
+ target?: string;
120
+ transition?: boolean;
121
+ push?: boolean;
122
+ replace?: boolean;
123
+ prefetch?: boolean;
124
+ ttl?: number;
125
+ rollback?: number;
126
+ onNavigate?: () => void;
127
+ onError?: (error: unknown) => void;
128
+ }> &
129
+ React.DetailedHTMLProps<
130
+ React.HTMLAttributes<HTMLAnchorElement>,
131
+ HTMLAnchorElement
132
+ >
133
+ >;
134
+ export const Refresh: React.FC<
135
+ React.PropsWithChildren<{
136
+ url?: string;
137
+ outlet?: string;
138
+ transition?: boolean;
139
+ prefetch?: boolean;
140
+ ttl?: number;
141
+ onRefresh?: () => void;
142
+ onError?: (error: unknown) => void;
143
+ }> &
144
+ React.DetailedHTMLProps<
145
+ React.HTMLAttributes<HTMLAnchorElement>,
146
+ HTMLAnchorElement
147
+ >
148
+ >;
149
+ }
150
+
151
+ declare module "@lazarv/react-server/remote-component" {
152
+ const RemoteComponent: React.FC<{
153
+ url: string;
154
+ outlet?: string;
155
+ ttl?: number;
156
+ request?: RequestInit;
157
+ }>;
158
+ export default RemoteComponent;
159
+ }
160
+
161
+ declare module "@lazarv/react-server/router" {
162
+ interface ReactServerRouting {
163
+ path: string;
164
+ }
165
+
166
+ export type RouteParams = Record<string, string | string[]>;
167
+ export type RouteMatchers = Record<string, (value: string) => boolean>;
168
+
169
+ export const ClientOnly: React.FC<React.PropsWithChildren>;
170
+ export const Route: React.FC<
171
+ React.PropsWithChildren<{
172
+ path: string;
173
+ exact?: boolean;
174
+ matchers?: RouteMatchers;
175
+ element?: React.ReactElement;
176
+ render?: (
177
+ props: React.PropsWithChildren<RouteParams>
178
+ ) => React.ReactElement;
179
+ standalone?: boolean;
180
+ remote?: boolean;
181
+ fallback?: boolean;
182
+ }>
183
+ >;
184
+
185
+ export type ActionState<T, E> = {
186
+ formData: FormData | null;
187
+ data: T | null;
188
+ error: E | null;
189
+ actionId: string | null;
190
+ };
191
+ export function useActionState<
192
+ T extends (
193
+ ...args: any[]
194
+ ) => T extends (...args: any[]) => infer R ? R : any,
195
+ E = Error,
196
+ >(action: T): ActionState<ReturnType<T>, E>;
197
+
198
+ export type MatchOptions = {
199
+ exact?: boolean;
200
+ fallback?: boolean;
201
+ matchers?: RouteMatchers;
202
+ };
203
+ export function useMatch<T = RouteParams>(
204
+ path: string,
205
+ options?: MatchOptions
206
+ ): T | null;
207
+
208
+ export function useParams<T = RouteParams>(): T;
209
+ }
@@ -0,0 +1,14 @@
1
+ import ErrorBoundary from "@lazarv/react-server/client/ErrorBoundary.jsx";
2
+ import { Suspense } from "react";
3
+
4
+ export default async function ReactServerErrorBoundary({
5
+ fallback = null,
6
+ children,
7
+ ...props
8
+ }) {
9
+ return (
10
+ <Suspense fallback={fallback}>
11
+ <ErrorBoundary {...props}>{children}</ErrorBoundary>
12
+ </Suspense>
13
+ );
14
+ }
@@ -0,0 +1,210 @@
1
+ import { useCache } from "@lazarv/react-server/memory-cache";
2
+ import { server$ } from "@lazarv/react-server/server/actions.mjs";
3
+ import { context$, getContext } from "@lazarv/react-server/server/context.mjs";
4
+ import { createFromFetch } from "react-server-dom-webpack/client.edge";
5
+
6
+ import ReactServerComponent from "../client/ReactServerComponent.jsx";
7
+ import { HTTP_CONTEXT } from "./symbols.mjs";
8
+
9
+ export const REMOTE_CACHE = Symbol.for("REMOTE_CACHE");
10
+
11
+ const streamOptions = ({ url, ttl, outlet, request }) => ({
12
+ async callServer(...args) {
13
+ const [id, [data]] = args;
14
+ const formData = new FormData();
15
+ Object.entries(data).forEach(([k, v]) => {
16
+ if (!k.startsWith("$ACTION_ID_")) {
17
+ formData.append(k, v);
18
+ }
19
+ });
20
+ formData.append(`$ACTION_ID_${id}`, "");
21
+ return useCache(
22
+ [url, REMOTE_CACHE],
23
+ () =>
24
+ getRemoteFlightResponse({
25
+ url,
26
+ ttl,
27
+ request: {
28
+ method: "POST",
29
+ body: formData,
30
+ headers: {
31
+ ...(request?.headers ?? {}),
32
+ accept: "text/x-component;standalone;remote",
33
+ outlet,
34
+ },
35
+ },
36
+ }),
37
+ ttl,
38
+ true
39
+ );
40
+ },
41
+ });
42
+ function proxyServerReference(parent, key, value, url, outlet) {
43
+ if (!value) return value;
44
+ else if (Array.isArray(value)) {
45
+ value.forEach((v, k) => proxyServerReference(value, k, v, url, outlet));
46
+ } else if (
47
+ value &&
48
+ typeof value === "object" &&
49
+ value.$$typeof?.toString() !== "Symbol(react.server_context)"
50
+ ) {
51
+ Object.entries(value).forEach(([k, v]) =>
52
+ proxyServerReference(value, k, v, url, outlet)
53
+ );
54
+ } else if (
55
+ typeof value === "function" &&
56
+ typeof value.$$FORM_ACTION === "function" &&
57
+ !value.$$remote_server_action
58
+ ) {
59
+ const proxy = server$((...args) => {
60
+ value(...args);
61
+ });
62
+ proxy.$$bound = [
63
+ {
64
+ __react_server_remote_component_url__: url,
65
+ __react_server_remote_component_outlet__: outlet,
66
+ },
67
+ ];
68
+ proxy.$$remote_server_action = true;
69
+ parent[key] = proxy;
70
+ }
71
+ }
72
+ async function getRemoteFlightResponse({ url, ttl, outlet, request = {} }) {
73
+ const Component = createFromFetch(
74
+ fetch(url, {
75
+ ...request,
76
+ headers: {
77
+ ...(request?.headers ?? {}),
78
+ accept: "text/x-component;standalone;remote",
79
+ outlet,
80
+ },
81
+ }),
82
+ streamOptions({ url, ttl, outlet, request })
83
+ );
84
+ const promise = new Promise((resolve, reject) => {
85
+ Component.then((value) => {
86
+ try {
87
+ proxyServerReference(null, null, value, url, outlet);
88
+ } catch (e) {
89
+ reject(e);
90
+ }
91
+ resolve(value);
92
+ });
93
+ });
94
+ promise._response = Component._response;
95
+ return Component;
96
+ }
97
+
98
+ function FlightComponent({ url, ttl = Infinity, outlet = null, request = {} }) {
99
+ const key = `__react_server_remote_component_outlet_${outlet}__`;
100
+ const accept = getContext(HTTP_CONTEXT).request.headers.get("accept");
101
+ const Component = useCache(
102
+ [url, accept, REMOTE_CACHE],
103
+ async () => {
104
+ const res = await fetch(url, {
105
+ ...request,
106
+ headers: {
107
+ ...(request?.headers ?? {}),
108
+ accept: `${accept};standalone;remote`,
109
+ outlet,
110
+ },
111
+ });
112
+
113
+ if (!res.ok) {
114
+ const [message, ...stack] = (await res.text()).split("\n");
115
+ const remoteError = new Error(
116
+ `Failed to load remote component: ${message.replace(
117
+ /^Error:\s*/,
118
+ ""
119
+ )}`
120
+ );
121
+ remoteError.stack = stack.map((l) => l.trim()).join("\n");
122
+ throw remoteError;
123
+ }
124
+
125
+ const reader = res.body.getReader();
126
+ const Component = { outlet, html: "", rsc: "" };
127
+
128
+ const decoder = new TextDecoder();
129
+ let done = false;
130
+ let value = "";
131
+ while (!done) {
132
+ const { value: chunk, done: _done } = await reader.read();
133
+ done = _done;
134
+ const str = decoder.decode(chunk);
135
+ value += str;
136
+ const lines = value.split("\n");
137
+ value = lines.pop();
138
+ for (const line of lines) {
139
+ if (/^[0-9a-f]+:/.test(line)) {
140
+ Component.rsc += line + "\n";
141
+ if (/^0:/.test(line)) {
142
+ if (!done) {
143
+ Component.stream = new ReadableStream({
144
+ async start(controller) {
145
+ let done = false;
146
+ let value = "";
147
+ let html = "";
148
+ let rsc = "";
149
+
150
+ while (!done) {
151
+ const { value: chunk, done: _done } = await reader.read();
152
+ done = _done;
153
+ if (chunk) {
154
+ const str = decoder.decode(chunk);
155
+ value += str;
156
+ const lines = value.split("\n");
157
+ value = lines.pop();
158
+ for (const line of lines) {
159
+ if (/^[0-9a-f]+:/.test(line)) {
160
+ rsc += line + "\n";
161
+ } else {
162
+ html += line;
163
+ }
164
+ }
165
+ if (!value && html && rsc) {
166
+ controller.enqueue({ outlet, html, rsc });
167
+ html = "";
168
+ rsc = "";
169
+ }
170
+ }
171
+ }
172
+
173
+ controller.enqueue({ outlet, html: html || value, rsc });
174
+ controller.close();
175
+ },
176
+ });
177
+ }
178
+ return Component;
179
+ }
180
+ } else {
181
+ Component.html += line;
182
+ }
183
+ }
184
+ }
185
+ if (/^[0-9a-f]+:/.test(value)) {
186
+ Component.rsc += value + "\n";
187
+ } else {
188
+ Component.html += value;
189
+ }
190
+ Component.outlet = outlet;
191
+ return Component;
192
+ },
193
+ ttl
194
+ );
195
+ context$(key, Component);
196
+ return <>{key}</>;
197
+ }
198
+
199
+ export default function RemoteComponent({
200
+ url,
201
+ outlet = null,
202
+ ttl = Infinity,
203
+ request = {},
204
+ }) {
205
+ return (
206
+ <ReactServerComponent url={url} outlet={outlet} standalone remote>
207
+ <FlightComponent url={url} ttl={ttl} outlet={outlet} request={request} />
208
+ </ReactServerComponent>
209
+ );
210
+ }
@@ -0,0 +1,108 @@
1
+ import { ParamsContext } from "@lazarv/react-server/client/Params.mjs";
2
+ import { context$, getContext } from "@lazarv/react-server/server/context.mjs";
3
+ import { useRequest, useUrl } from "@lazarv/react-server/server/request.mjs";
4
+ import { ROUTE_MATCH } from "@lazarv/react-server/server/symbols.mjs";
5
+
6
+ export function useMatch(path, options = {}) {
7
+ if (path === "*" || options.fallback) {
8
+ if (getContext(ROUTE_MATCH)) {
9
+ return null;
10
+ }
11
+ return {};
12
+ }
13
+
14
+ const paramToArray = new Set();
15
+ const paramMatch = new Set();
16
+ const { pathname: rawPathname } = useUrl();
17
+ const pathname = decodeURIComponent(rawPathname);
18
+ const regexp =
19
+ path instanceof RegExp
20
+ ? path
21
+ : new RegExp(
22
+ path === "/" && !options.exact
23
+ ? "^\\/(.*)$"
24
+ : `^${path
25
+ .replace(/\[u\+([^\]]+)\]/g, (_, code) => `\\u${code}`)
26
+ .replace(/\/?\[(\[?[^\]]+\]?)\]/g, (_, name) => {
27
+ let optional = false;
28
+ if (name[0] === "[" && name[name.length - 1] === "]") {
29
+ name = name.slice(1, -1);
30
+ optional = true;
31
+ }
32
+ if (name.startsWith("...")) {
33
+ name = name.slice(3);
34
+ paramToArray.add(name);
35
+ return `(\\/${optional ? "?" : ""}(?<${name}>.*))${
36
+ optional ? "*" : "+"
37
+ }`;
38
+ }
39
+ if (name.includes("=")) {
40
+ const [paramName, matcher] = name.split("=");
41
+ name = paramName;
42
+ paramMatch.add({ name, matcher });
43
+ }
44
+ return `\\/?(?<${name}>[^/]+)${optional ? "?" : ""}`;
45
+ })}${options.exact ? "$" : "(\\/([^/]+)?)*$"}`
46
+ );
47
+
48
+ const match = pathname.match(regexp);
49
+ if (!match) return null;
50
+
51
+ for (const { name, matcher } of paramMatch.values()) {
52
+ if (!options.matchers?.[matcher]?.(match.groups[name])) return null;
53
+ }
54
+
55
+ return {
56
+ ...match.groups,
57
+ ...Array.from(paramToArray.values()).reduce((obj, key) => {
58
+ obj[key] = match.groups[key]?.split("/") ?? [];
59
+ return obj;
60
+ }, {}),
61
+ };
62
+ }
63
+
64
+ export default function Route({
65
+ path,
66
+ exact,
67
+ matchers,
68
+ element,
69
+ render,
70
+ standalone,
71
+ remote,
72
+ fallback,
73
+ children,
74
+ }) {
75
+ const { headers } = useRequest();
76
+ const params = useMatch(path, { exact, matchers, fallback });
77
+
78
+ if (!params) return null;
79
+
80
+ if (standalone !== false) {
81
+ context$(ROUTE_MATCH, params);
82
+ }
83
+
84
+ const accept = headers.get("accept");
85
+ const acceptStandalone = accept?.includes(";standalone");
86
+ const acceptRemote = accept?.includes(";remote");
87
+
88
+ if (acceptStandalone && standalone === false) return <>{children}</>;
89
+ if ((remote === false && acceptRemote) || (remote === true && !acceptRemote))
90
+ return null;
91
+
92
+ const hasParams = Reflect.ownKeys(params).length > 0;
93
+ if (render)
94
+ return hasParams ? (
95
+ <ParamsContext.Provider value={params}>
96
+ {render({ ...params, children })}
97
+ </ParamsContext.Provider>
98
+ ) : (
99
+ render({ ...params, children })
100
+ );
101
+ if (element)
102
+ return hasParams ? (
103
+ <ParamsContext.Provider value={params}>{element}</ParamsContext.Provider>
104
+ ) : (
105
+ element
106
+ );
107
+ return null;
108
+ }
@@ -0,0 +1,72 @@
1
+ import { AsyncLocalStorage } from "node:async_hooks";
2
+ import { createHash } from "node:crypto";
3
+
4
+ export const asyncLocalStorage = (globalThis.__react_server_actions__ =
5
+ globalThis.__react_server_actions__ || new AsyncLocalStorage());
6
+
7
+ export function ref$(type, map, fn) {
8
+ let id;
9
+ try {
10
+ throw new Error();
11
+ } catch (e) {
12
+ const hash = createHash("md5");
13
+ hash.update(e.stack.replaceAll(/\n\s*/g, " "));
14
+ id = hash.digest("hex");
15
+ }
16
+
17
+ const { actionId } = asyncLocalStorage.getStore() ?? {};
18
+ if (actionId === id && map.has(id)) {
19
+ throw Promise.reject();
20
+ }
21
+
22
+ if (map.has(id)) {
23
+ return map.get(id);
24
+ }
25
+
26
+ const proxy = new Proxy(fn, {
27
+ get(target, prop, receiver) {
28
+ if (prop === "bind") {
29
+ return (thisArg, ...args) => {
30
+ const bound = target.bind(thisArg, ...args);
31
+ bound.$$id = id;
32
+ bound.$$typeof = type;
33
+ bound.$$async = true;
34
+ bound.$$bound = thisArg;
35
+ bound.$$FORM_ACTION = (name) => {
36
+ const data = new FormData();
37
+ return {
38
+ name,
39
+ method: "POST",
40
+ encType: "multipart/form-data",
41
+ data,
42
+ };
43
+ };
44
+ map.set(bound.$$id, bound);
45
+ return bound;
46
+ };
47
+ }
48
+ return Reflect.get(target, prop, receiver);
49
+ },
50
+ apply(target, thisArg, args) {
51
+ return target.apply(thisArg, args);
52
+ },
53
+ });
54
+
55
+ const bound = proxy.bind(null);
56
+ if (actionId === id) {
57
+ throw Promise.reject();
58
+ }
59
+ return bound;
60
+ }
61
+
62
+ export const serverReferenceMap = new Map();
63
+ export function server$(fn) {
64
+ if (typeof window !== "undefined") {
65
+ throw new Error("server$ can only be used on the server");
66
+ }
67
+ return ref$(Symbol.for("react.server.reference"), serverReferenceMap, fn);
68
+ }
69
+ export async function callServerReference(id, ...args) {
70
+ const proxy = serverReferenceMap.get(id);
71
+ return proxy.apply(proxy.$$bound, args);
72
+ }
@@ -0,0 +1,19 @@
1
+ import { getContext } from "@lazarv/react-server/server/context.mjs";
2
+
3
+ import { useRequest, useUrl } from "./request.mjs";
4
+ import { CACHE_CONTEXT, FLIGHT_CACHE, HTML_CACHE } from "./symbols.mjs";
5
+
6
+ export function cache$(Component, ttl = Infinity) {
7
+ return (props) => {
8
+ const url = useUrl()?.toString();
9
+ const accept = useRequest()?.headers?.get?.("accept");
10
+ const cache = getContext(CACHE_CONTEXT);
11
+ if (ttl === true) {
12
+ ttl = Infinity;
13
+ }
14
+ const expiry = Date.now() + ttl;
15
+ cache.setExpiry([url, accept, FLIGHT_CACHE], expiry);
16
+ cache.setExpiry([url, accept, HTML_CACHE], expiry);
17
+ return Component(props);
18
+ };
19
+ }
@@ -0,0 +1,62 @@
1
+ import { context$, getContext } from "@lazarv/react-server/server/context.mjs";
2
+ import {
3
+ CLIENT_COMPONENTS,
4
+ HTTP_CONTEXT,
5
+ MANIFEST,
6
+ } from "@lazarv/react-server/server/symbols.mjs";
7
+
8
+ export const clientCache = (globalThis.__react_server_client_components__ =
9
+ globalThis.__react_server_client_components__ || new Map());
10
+
11
+ export const clientReferenceMap = new Proxy(
12
+ {},
13
+ {
14
+ get(_, $$id) {
15
+ let def = clientCache.get($$id);
16
+ const [id, name = "default"] = $$id.split("::");
17
+ if (!clientCache.has($$id)) {
18
+ const manifest = getContext(MANIFEST);
19
+ if (!manifest) {
20
+ def = {
21
+ id,
22
+ chunks: [],
23
+ name,
24
+ async: true,
25
+ };
26
+ } else {
27
+ const { client, server } = manifest;
28
+ const filename = id.replace(`${process.cwd()}/.react-server/`, "");
29
+ const serverEntry = Object.values(server).find(
30
+ (entry) => entry.file === filename
31
+ );
32
+ const clientEntry = Object.values(client).find((entry) =>
33
+ serverEntry.src.endsWith(entry.src)
34
+ );
35
+
36
+ const httpContext = getContext(HTTP_CONTEXT);
37
+ const origin =
38
+ httpContext?.request?.headers?.get("x-forwarded-for") ||
39
+ new URL(httpContext?.url).origin;
40
+ def = {
41
+ id: `${origin}/${clientEntry.file}`,
42
+ chunks: [],
43
+ name,
44
+ async: true,
45
+ };
46
+ }
47
+ clientCache.set($$id, def);
48
+ }
49
+
50
+ let clientComponents = getContext(CLIENT_COMPONENTS);
51
+ if (!clientComponents) {
52
+ clientComponents = new Set();
53
+ context$(CLIENT_COMPONENTS, clientComponents);
54
+ }
55
+ if (!clientComponents.has(def.id)) {
56
+ clientComponents.add(def.id);
57
+ }
58
+
59
+ return def;
60
+ },
61
+ }
62
+ );