forked from web-infra-dev/modern.js
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcallServer.ts
More file actions
149 lines (125 loc) · 3.59 KB
/
callServer.ts
File metadata and controls
149 lines (125 loc) · 3.59 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
import {
createFromFetch,
encodeReply,
} from 'react-server-dom-rspack/client.browser';
type ReactServerValue = unknown;
export type ActionIdResolver = (id: string) => string | Promise<string>;
export type ActionRequestUrlResolver = (entryName?: string) => string;
let actionIdResolver: ActionIdResolver | undefined;
let actionRequestUrlResolver: ActionRequestUrlResolver | undefined;
/**
* Register a custom action ID resolver. Plugins (e.g. Module Federation)
* use this to remap raw action IDs before they are sent to the server.
*/
export const setResolveActionId = (resolver?: ActionIdResolver): void => {
actionIdResolver = resolver;
};
export const setActionIdResolver = setResolveActionId;
const resolveActionId = (id: string): string | Promise<string> => {
const resolver = actionIdResolver;
if (typeof resolver === 'function') {
return resolver(id);
}
return id;
};
/**
* Register a custom action request URL resolver. Plugins can use this
* to align request URLs with customized route/base configurations.
*/
export const setResolveActionRequestUrl = (
resolver?: ActionRequestUrlResolver,
): void => {
actionRequestUrlResolver = resolver;
};
export const setActionRequestUrlResolver = setResolveActionRequestUrl;
const resolveActionRequestUrl = (): string => {
const entryName =
typeof window !== 'undefined' ? window.__MODERN_JS_ENTRY_NAME : undefined;
const resolver = actionRequestUrlResolver;
if (typeof resolver === 'function') {
return resolver(entryName);
}
// Legacy fallback: preserve existing behavior for default entry names.
if (!entryName || entryName === 'main' || entryName === 'index') {
return '/';
}
return `/${entryName}`;
};
class CallServerError extends Error {
readonly #statusCode: number;
readonly #url: string;
readonly #details: Record<string, unknown>;
constructor(
message: string,
statusCode: number,
url = '',
details: Record<string, unknown> = {},
) {
const formattedMessage = `Call Server Action failed (${statusCode}): ${message}\nURL: ${url}`;
super(formattedMessage);
this.name = 'CallServerError';
this.#statusCode = statusCode;
this.#url = url;
this.#details = details;
}
get statusCode(): number {
return this.#statusCode;
}
get url(): string {
return this.#url;
}
get details(): Record<string, unknown> {
return { ...this.#details };
}
toJSON() {
return {
name: this.name,
message: this.message,
statusCode: this.#statusCode,
url: this.#url,
details: this.#details,
};
}
}
export async function requestCallServer(id: string, args: ReactServerValue) {
let url = '/';
let actionId = id;
try {
url = resolveActionRequestUrl();
actionId = await resolveActionId(id);
const response = await fetch(url, {
method: 'POST',
headers: {
Accept: 'text/x-component',
'x-rsc-action': actionId,
},
body: await encodeReply(args),
});
if (!response.ok) {
throw new CallServerError(response.statusText, response.status, url, {
id: actionId,
rawId: id,
args,
});
}
return response;
} catch (error) {
if (error instanceof CallServerError) {
throw error;
}
throw new CallServerError(
error instanceof Error ? error.message : 'Unknown error',
1,
url,
{ id: actionId, rawId: id, args },
);
}
}
export async function callServer(
id: string,
args: ReactServerValue,
): Promise<any> {
const response = requestCallServer(id, args);
const res = createFromFetch(response);
return res;
}