@reckona/mreact-router 0.0.202 → 0.0.204
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/README.md +58 -11
- package/dist/actions.d.ts +8 -3
- package/dist/actions.d.ts.map +1 -1
- package/dist/actions.js +125 -45
- package/dist/actions.js.map +1 -1
- package/dist/adapters/aws-lambda.d.ts +15 -4
- package/dist/adapters/aws-lambda.d.ts.map +1 -1
- package/dist/adapters/aws-lambda.js +301 -63
- package/dist/adapters/aws-lambda.js.map +1 -1
- package/dist/adapters/cloudflare.d.ts +5 -2
- package/dist/adapters/cloudflare.d.ts.map +1 -1
- package/dist/adapters/cloudflare.js +35 -11
- package/dist/adapters/cloudflare.js.map +1 -1
- package/dist/adapters/edge.d.ts +1 -1
- package/dist/adapters/edge.d.ts.map +1 -1
- package/dist/adapters/edge.js.map +1 -1
- package/dist/adapters/node.d.ts +5 -3
- package/dist/adapters/node.d.ts.map +1 -1
- package/dist/adapters/node.js +2 -1
- package/dist/adapters/node.js.map +1 -1
- package/dist/adapters/static.d.ts.map +1 -1
- package/dist/adapters/static.js +24 -2
- package/dist/adapters/static.js.map +1 -1
- package/dist/build.d.ts +4 -1
- package/dist/build.d.ts.map +1 -1
- package/dist/build.js +185 -13
- package/dist/build.js.map +1 -1
- package/dist/built-runtime.d.ts +2 -0
- package/dist/built-runtime.d.ts.map +1 -1
- package/dist/built-runtime.js +16 -0
- package/dist/built-runtime.js.map +1 -1
- package/dist/cache.d.ts +4 -2
- package/dist/cache.d.ts.map +1 -1
- package/dist/cache.js +216 -17
- package/dist/cache.js.map +1 -1
- package/dist/cli-options.d.ts +3 -0
- package/dist/cli-options.d.ts.map +1 -1
- package/dist/cli-options.js +10 -2
- package/dist/cli-options.js.map +1 -1
- package/dist/cli.js +14 -3
- package/dist/cli.js.map +1 -1
- package/dist/client.d.ts +5 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +43 -3
- package/dist/client.js.map +1 -1
- package/dist/config.d.ts +3 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +21 -2
- package/dist/config.js.map +1 -1
- package/dist/csp.d.ts +1 -0
- package/dist/csp.d.ts.map +1 -1
- package/dist/csp.js +9 -0
- package/dist/csp.js.map +1 -1
- package/dist/dehydrate-policy.d.ts +3 -0
- package/dist/dehydrate-policy.d.ts.map +1 -0
- package/dist/dehydrate-policy.js +16 -0
- package/dist/dehydrate-policy.js.map +1 -0
- package/dist/dev-server.d.ts +7 -2
- package/dist/dev-server.d.ts.map +1 -1
- package/dist/dev-server.js +120 -11
- package/dist/dev-server.js.map +1 -1
- package/dist/http.d.ts +1 -1
- package/dist/http.d.ts.map +1 -1
- package/dist/http.js +147 -7
- package/dist/http.js.map +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/logger.d.ts +22 -1
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js +22 -8
- package/dist/logger.js.map +1 -1
- package/dist/metadata.d.ts +8 -0
- package/dist/metadata.d.ts.map +1 -1
- package/dist/metadata.js +60 -22
- package/dist/metadata.js.map +1 -1
- package/dist/navigation-marker.d.ts +2 -0
- package/dist/navigation-marker.d.ts.map +1 -0
- package/dist/navigation-marker.js +357 -0
- package/dist/navigation-marker.js.map +1 -0
- package/dist/navigation-runtime.d.ts +1 -1
- package/dist/navigation-runtime.d.ts.map +1 -1
- package/dist/navigation-runtime.js +1 -1
- package/dist/navigation-runtime.js.map +1 -1
- package/dist/navigation.d.ts +1 -0
- package/dist/navigation.d.ts.map +1 -1
- package/dist/navigation.js +1 -1
- package/dist/navigation.js.map +1 -1
- package/dist/node-server.d.ts +5 -2
- package/dist/node-server.d.ts.map +1 -1
- package/dist/node-server.js +75 -6
- package/dist/node-server.js.map +1 -1
- package/dist/prerender-entry.d.ts +17 -1
- package/dist/prerender-entry.d.ts.map +1 -1
- package/dist/prerender-entry.js +98 -3
- package/dist/prerender-entry.js.map +1 -1
- package/dist/render.d.ts +16 -2
- package/dist/render.d.ts.map +1 -1
- package/dist/render.js +142 -135
- package/dist/render.js.map +1 -1
- package/dist/serve.d.ts +11 -2
- package/dist/serve.d.ts.map +1 -1
- package/dist/serve.js +67 -41
- package/dist/serve.js.map +1 -1
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +52 -19
- package/dist/session.js.map +1 -1
- package/dist/upgrade.d.ts +59 -2
- package/dist/upgrade.d.ts.map +1 -1
- package/dist/upgrade.js +282 -1
- package/dist/upgrade.js.map +1 -1
- package/dist/vite-config.d.ts +2 -0
- package/dist/vite-config.d.ts.map +1 -1
- package/dist/vite-config.js +2 -0
- package/dist/vite-config.js.map +1 -1
- package/dist/vite.d.ts +4 -1
- package/dist/vite.d.ts.map +1 -1
- package/dist/vite.js +117 -6
- package/dist/vite.js.map +1 -1
- package/package.json +11 -11
- package/src/actions.ts +146 -60
- package/src/adapters/aws-lambda.ts +426 -115
- package/src/adapters/cloudflare.ts +58 -12
- package/src/adapters/edge.ts +2 -0
- package/src/adapters/node.ts +8 -10
- package/src/adapters/static.ts +31 -2
- package/src/build.ts +217 -13
- package/src/built-runtime.ts +28 -0
- package/src/cache.ts +272 -19
- package/src/cli-options.ts +15 -2
- package/src/cli.ts +16 -2
- package/src/client.ts +47 -3
- package/src/config.ts +43 -6
- package/src/csp.ts +11 -0
- package/src/dehydrate-policy.ts +23 -0
- package/src/dev-server.ts +188 -15
- package/src/http.ts +172 -6
- package/src/index.ts +14 -1
- package/src/logger.ts +47 -10
- package/src/metadata.ts +125 -60
- package/src/navigation-marker.ts +423 -0
- package/src/navigation-runtime.ts +1 -0
- package/src/navigation.ts +1 -1
- package/src/node-server.ts +128 -22
- package/src/prerender-entry.ts +127 -5
- package/src/render.ts +859 -749
- package/src/serve.ts +112 -82
- package/src/session.ts +56 -25
- package/src/upgrade.ts +402 -3
- package/src/vite-config.ts +6 -0
- package/src/vite.ts +157 -5
package/src/upgrade.ts
CHANGED
|
@@ -1,11 +1,410 @@
|
|
|
1
|
-
import type { IncomingMessage } from "node:http";
|
|
1
|
+
import type { IncomingMessage, Server } from "node:http";
|
|
2
2
|
import type { Duplex } from "node:stream";
|
|
3
|
+
import {
|
|
4
|
+
emitRouterLog,
|
|
5
|
+
logDurationMs,
|
|
6
|
+
logError,
|
|
7
|
+
logNow,
|
|
8
|
+
nodeRequestPath,
|
|
9
|
+
type AppRouterLogger,
|
|
10
|
+
} from "./logger.js";
|
|
11
|
+
|
|
12
|
+
export type HttpUpgradeDisposition = "declined" | "handled";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Explicitly claims or declines one HTTP upgrade before asynchronous verification begins.
|
|
16
|
+
*/
|
|
17
|
+
export interface HttpUpgradeContext {
|
|
18
|
+
accept(): HttpUpgradeDisposition;
|
|
19
|
+
decline(): HttpUpgradeDisposition;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** Handles an HTTP upgrade using the legacy three-argument callback contract. */
|
|
23
|
+
export type HttpUpgradeHandler = (request: IncomingMessage, socket: Duplex, head: Buffer) => void;
|
|
3
24
|
|
|
4
25
|
/**
|
|
5
|
-
* Handles
|
|
26
|
+
* Handles a managed HTTP upgrade on the app-router Node server.
|
|
27
|
+
*
|
|
28
|
+
* Existing `HttpUpgradeHandler` callbacks remain assignable. Handlers that defer verification must
|
|
29
|
+
* call `context.accept()` synchronously; returning without writing a handshake is treated as a
|
|
30
|
+
* decline.
|
|
6
31
|
*/
|
|
7
|
-
export type
|
|
32
|
+
export type ManagedHttpUpgradeHandler = (
|
|
8
33
|
request: IncomingMessage,
|
|
9
34
|
socket: Duplex,
|
|
10
35
|
head: Buffer,
|
|
36
|
+
context: HttpUpgradeContext,
|
|
11
37
|
) => void;
|
|
38
|
+
|
|
39
|
+
export type HttpUpgradeOriginFailureReason =
|
|
40
|
+
| "disallowed-origin"
|
|
41
|
+
| "malformed-origin"
|
|
42
|
+
| "missing-origin"
|
|
43
|
+
| "opaque-origin";
|
|
44
|
+
|
|
45
|
+
export type HttpUpgradeOriginValidation =
|
|
46
|
+
| { ok: true; origin: string | undefined }
|
|
47
|
+
| { ok: false; reason: HttpUpgradeOriginFailureReason };
|
|
48
|
+
|
|
49
|
+
export interface ValidateHttpUpgradeOriginOptions {
|
|
50
|
+
allowedOrigins: readonly string[];
|
|
51
|
+
allowMissingOrigin?: boolean | undefined;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export type HttpUpgradeOriginPolicy =
|
|
55
|
+
| "same-origin"
|
|
56
|
+
| "unchecked"
|
|
57
|
+
| {
|
|
58
|
+
allowedOrigins: readonly string[];
|
|
59
|
+
allowMissingOrigin?: boolean | undefined;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
export function assertValidHttpUpgradeOriginPolicy(
|
|
63
|
+
policy: HttpUpgradeOriginPolicy | undefined,
|
|
64
|
+
): void {
|
|
65
|
+
if (typeof policy === "object") {
|
|
66
|
+
for (const origin of policy.allowedOrigins) {
|
|
67
|
+
normalizeConfiguredOrigin(origin);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Validates a WebSocket/HTTP upgrade Origin against exact serialized origins.
|
|
74
|
+
*
|
|
75
|
+
* Missing origins are rejected by default. Configured origins must contain only an HTTP(S)
|
|
76
|
+
* scheme, host, and optional port; paths, credentials, queries, and fragments are invalid.
|
|
77
|
+
*/
|
|
78
|
+
export function validateHttpUpgradeOrigin(
|
|
79
|
+
request: IncomingMessage,
|
|
80
|
+
options: ValidateHttpUpgradeOriginOptions,
|
|
81
|
+
): HttpUpgradeOriginValidation {
|
|
82
|
+
const allowedOrigins = options.allowedOrigins.map(normalizeConfiguredOrigin);
|
|
83
|
+
const value = request.headers.origin;
|
|
84
|
+
|
|
85
|
+
if (value === undefined) {
|
|
86
|
+
return options.allowMissingOrigin === true
|
|
87
|
+
? { ok: true, origin: undefined }
|
|
88
|
+
: { ok: false, reason: "missing-origin" };
|
|
89
|
+
}
|
|
90
|
+
if (Array.isArray(value) || value.includes(",")) {
|
|
91
|
+
return { ok: false, reason: "malformed-origin" };
|
|
92
|
+
}
|
|
93
|
+
if (value === "null") {
|
|
94
|
+
return { ok: false, reason: "opaque-origin" };
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const origin = normalizeRequestOrigin(value);
|
|
98
|
+
if (origin === undefined) {
|
|
99
|
+
return { ok: false, reason: "malformed-origin" };
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return allowedOrigins.includes(origin)
|
|
103
|
+
? { ok: true, origin }
|
|
104
|
+
: { ok: false, reason: "disallowed-origin" };
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function normalizeConfiguredOrigin(value: string): string {
|
|
108
|
+
const origin = normalizeOrigin(value);
|
|
109
|
+
if (origin === undefined) {
|
|
110
|
+
throw new TypeError(`Invalid HTTP upgrade allowed origin: ${value}`);
|
|
111
|
+
}
|
|
112
|
+
return origin;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function normalizeRequestOrigin(value: string): string | undefined {
|
|
116
|
+
return normalizeOrigin(value);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function normalizeOrigin(value: string): string | undefined {
|
|
120
|
+
let url: URL;
|
|
121
|
+
try {
|
|
122
|
+
url = new URL(value);
|
|
123
|
+
} catch {
|
|
124
|
+
return undefined;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (
|
|
128
|
+
(url.protocol !== "http:" && url.protocol !== "https:") ||
|
|
129
|
+
url.username !== "" ||
|
|
130
|
+
url.password !== "" ||
|
|
131
|
+
url.pathname !== "/" ||
|
|
132
|
+
url.search !== "" ||
|
|
133
|
+
url.hash !== "" ||
|
|
134
|
+
url.origin === "null"
|
|
135
|
+
) {
|
|
136
|
+
return undefined;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return url.origin;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export interface ManagedHttpUpgradeOptions {
|
|
143
|
+
decisionTimeoutMs: number;
|
|
144
|
+
handler: ManagedHttpUpgradeHandler;
|
|
145
|
+
isOriginAllowed(request: IncomingMessage): HttpUpgradeOriginValidation;
|
|
146
|
+
logger?: AppRouterLogger | undefined;
|
|
147
|
+
shouldBypass?: ((request: IncomingMessage) => boolean) | undefined;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export interface ManagedHttpUpgradeLifecycle {
|
|
151
|
+
beginClose(): void;
|
|
152
|
+
handle(request: IncomingMessage, socket: Duplex, head: Buffer): void;
|
|
153
|
+
waitForSockets(timeoutMs: number): Promise<void>;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export function createManagedHttpUpgradeLifecycle(
|
|
157
|
+
options: ManagedHttpUpgradeOptions,
|
|
158
|
+
): ManagedHttpUpgradeLifecycle {
|
|
159
|
+
const sockets = new Set<Duplex>();
|
|
160
|
+
const emptyWaiters = new Set<() => void>();
|
|
161
|
+
const pendingDecisionCancels = new Set<() => void>();
|
|
162
|
+
let closing = false;
|
|
163
|
+
|
|
164
|
+
function track(socket: Duplex): void {
|
|
165
|
+
sockets.add(socket);
|
|
166
|
+
socket.once("close", () => {
|
|
167
|
+
sockets.delete(socket);
|
|
168
|
+
if (sockets.size === 0) {
|
|
169
|
+
for (const resolve of emptyWaiters) {
|
|
170
|
+
resolve();
|
|
171
|
+
}
|
|
172
|
+
emptyWaiters.clear();
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function destroy(socket: Duplex): void {
|
|
178
|
+
if (!socket.destroyed) {
|
|
179
|
+
socket.destroy();
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function emitError(request: IncomingMessage, startedAt: number, error: unknown): void {
|
|
184
|
+
emitRouterLog(options.logger, "error", {
|
|
185
|
+
durationMs: logDurationMs(startedAt),
|
|
186
|
+
error: logError(error),
|
|
187
|
+
method: request.method ?? "GET",
|
|
188
|
+
path: nodeRequestPath(request.url),
|
|
189
|
+
runtime: "node",
|
|
190
|
+
type: "router:upgrade:error",
|
|
191
|
+
});
|
|
192
|
+
if (options.logger === undefined) {
|
|
193
|
+
try {
|
|
194
|
+
console.error("[mreact] HTTP upgrade handler failed:", error);
|
|
195
|
+
} catch {
|
|
196
|
+
// Failure reporting must never escape the HTTP upgrade listener.
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function handle(request: IncomingMessage, socket: Duplex, head: Buffer): void {
|
|
202
|
+
const startedAt = logNow();
|
|
203
|
+
if (closing) {
|
|
204
|
+
destroy(socket);
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
let bypass = false;
|
|
208
|
+
try {
|
|
209
|
+
bypass = options.shouldBypass?.(request) === true;
|
|
210
|
+
} catch (error) {
|
|
211
|
+
emitError(request, startedAt, error);
|
|
212
|
+
destroy(socket);
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
if (bypass) {
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
track(socket);
|
|
220
|
+
|
|
221
|
+
let origin: HttpUpgradeOriginValidation;
|
|
222
|
+
try {
|
|
223
|
+
origin = options.isOriginAllowed(request);
|
|
224
|
+
} catch (error) {
|
|
225
|
+
emitError(request, startedAt, error);
|
|
226
|
+
destroy(socket);
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
if (!origin.ok) {
|
|
230
|
+
emitRouterLog(options.logger, "warn", {
|
|
231
|
+
method: request.method ?? "GET",
|
|
232
|
+
path: nodeRequestPath(request.url),
|
|
233
|
+
reason: origin.reason,
|
|
234
|
+
runtime: "node",
|
|
235
|
+
type: "router:upgrade:rejected",
|
|
236
|
+
});
|
|
237
|
+
destroy(socket);
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
let disposition: HttpUpgradeDisposition | undefined;
|
|
242
|
+
let decisionTimer: ReturnType<typeof setTimeout> | undefined;
|
|
243
|
+
let failureReported = false;
|
|
244
|
+
let socketClosed = false;
|
|
245
|
+
const clearDecisionTimer = (): void => {
|
|
246
|
+
if (decisionTimer !== undefined) {
|
|
247
|
+
clearTimeout(decisionTimer);
|
|
248
|
+
decisionTimer = undefined;
|
|
249
|
+
}
|
|
250
|
+
pendingDecisionCancels.delete(clearDecisionTimer);
|
|
251
|
+
};
|
|
252
|
+
socket.once("close", () => {
|
|
253
|
+
socketClosed = true;
|
|
254
|
+
clearDecisionTimer();
|
|
255
|
+
});
|
|
256
|
+
const before = {
|
|
257
|
+
bytesWritten: socketBytesWritten(socket),
|
|
258
|
+
destroyed: socket.destroyed,
|
|
259
|
+
writableEnded: socket.writableEnded,
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
const decide = (next: HttpUpgradeDisposition): HttpUpgradeDisposition => {
|
|
263
|
+
if (disposition !== undefined) {
|
|
264
|
+
return disposition;
|
|
265
|
+
}
|
|
266
|
+
disposition = next;
|
|
267
|
+
clearDecisionTimer();
|
|
268
|
+
if (next === "declined") {
|
|
269
|
+
destroy(socket);
|
|
270
|
+
}
|
|
271
|
+
return next;
|
|
272
|
+
};
|
|
273
|
+
const context: HttpUpgradeContext = {
|
|
274
|
+
accept: () => decide("handled"),
|
|
275
|
+
decline: () => decide("declined"),
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
const fail = (error: unknown): void => {
|
|
279
|
+
if (failureReported) {
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
failureReported = true;
|
|
283
|
+
clearDecisionTimer();
|
|
284
|
+
emitError(request, startedAt, error);
|
|
285
|
+
disposition ??= "declined";
|
|
286
|
+
destroy(socket);
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
let result: unknown;
|
|
290
|
+
try {
|
|
291
|
+
result = (
|
|
292
|
+
options.handler as (
|
|
293
|
+
request: IncomingMessage,
|
|
294
|
+
socket: Duplex,
|
|
295
|
+
head: Buffer,
|
|
296
|
+
context: HttpUpgradeContext,
|
|
297
|
+
) => unknown
|
|
298
|
+
)(request, socket, head, context);
|
|
299
|
+
} catch (error) {
|
|
300
|
+
fail(error);
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
if ((typeof result === "object" || typeof result === "function") && result !== null) {
|
|
305
|
+
if (disposition === undefined && !closing && !socketClosed) {
|
|
306
|
+
decisionTimer = setTimeout(() => {
|
|
307
|
+
if (disposition === undefined) {
|
|
308
|
+
fail(
|
|
309
|
+
new Error(`HTTP upgrade decision timed out after ${options.decisionTimeoutMs}ms.`),
|
|
310
|
+
);
|
|
311
|
+
}
|
|
312
|
+
}, options.decisionTimeoutMs);
|
|
313
|
+
decisionTimer.unref?.();
|
|
314
|
+
pendingDecisionCancels.add(clearDecisionTimer);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
void Promise.resolve(result as PromiseLike<HttpUpgradeDisposition | void>).then(
|
|
318
|
+
(value) => {
|
|
319
|
+
if (socketClosed) {
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
if (value === "declined" || value === "handled") {
|
|
323
|
+
decide(value);
|
|
324
|
+
} else if (disposition === undefined) {
|
|
325
|
+
decide(legacySocketAction(socket, before) ? "handled" : "declined");
|
|
326
|
+
}
|
|
327
|
+
},
|
|
328
|
+
(error) => {
|
|
329
|
+
fail(error);
|
|
330
|
+
},
|
|
331
|
+
);
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
if (result === "declined" || result === "handled") {
|
|
336
|
+
decide(result);
|
|
337
|
+
} else if (disposition === undefined) {
|
|
338
|
+
decide(legacySocketAction(socket, before) ? "handled" : "declined");
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
return {
|
|
343
|
+
beginClose() {
|
|
344
|
+
if (closing) {
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
closing = true;
|
|
348
|
+
for (const cancel of pendingDecisionCancels) {
|
|
349
|
+
cancel();
|
|
350
|
+
}
|
|
351
|
+
},
|
|
352
|
+
handle,
|
|
353
|
+
async waitForSockets(timeoutMs) {
|
|
354
|
+
if (sockets.size === 0) {
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
await new Promise<void>((resolve) => {
|
|
359
|
+
let settled = false;
|
|
360
|
+
const finish = () => {
|
|
361
|
+
if (settled) {
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
settled = true;
|
|
365
|
+
clearTimeout(timer);
|
|
366
|
+
emptyWaiters.delete(finish);
|
|
367
|
+
resolve();
|
|
368
|
+
};
|
|
369
|
+
const timer = setTimeout(() => {
|
|
370
|
+
for (const socket of sockets) {
|
|
371
|
+
destroy(socket);
|
|
372
|
+
}
|
|
373
|
+
finish();
|
|
374
|
+
}, timeoutMs);
|
|
375
|
+
timer.unref?.();
|
|
376
|
+
emptyWaiters.add(finish);
|
|
377
|
+
});
|
|
378
|
+
},
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
export function closeServerWithUpgrades(options: {
|
|
383
|
+
lifecycle: ManagedHttpUpgradeLifecycle;
|
|
384
|
+
server: Server;
|
|
385
|
+
timeoutMs: number;
|
|
386
|
+
}): Promise<void> {
|
|
387
|
+
options.lifecycle.beginClose();
|
|
388
|
+
const serverClosed = new Promise<void>((resolve, reject) => {
|
|
389
|
+
options.server.close((error) => (error ? reject(error) : resolve()));
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
return Promise.all([serverClosed, options.lifecycle.waitForSockets(options.timeoutMs)]).then(
|
|
393
|
+
() => undefined,
|
|
394
|
+
);
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
function legacySocketAction(
|
|
398
|
+
socket: Duplex,
|
|
399
|
+
before: { bytesWritten: number | undefined; destroyed: boolean; writableEnded: boolean },
|
|
400
|
+
): boolean {
|
|
401
|
+
return (
|
|
402
|
+
socketBytesWritten(socket) !== before.bytesWritten ||
|
|
403
|
+
socket.destroyed !== before.destroyed ||
|
|
404
|
+
socket.writableEnded !== before.writableEnded
|
|
405
|
+
);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
function socketBytesWritten(socket: Duplex): number | undefined {
|
|
409
|
+
return (socket as Duplex & { bytesWritten?: number }).bytesWritten;
|
|
410
|
+
}
|
package/src/vite-config.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { loadConfigFromFile, type ConfigEnv, type PluginOption, type UserConfig } from "vite";
|
|
2
|
+
import type { DehydrateOptions } from "@reckona/mreact-query";
|
|
2
3
|
import type { ResolvedAppRouterProject } from "./config.js";
|
|
3
4
|
import type { AppRouterImportPolicy } from "./import-policy.js";
|
|
4
5
|
import { mreactRouterConfigFromPlugins } from "./vite.js";
|
|
5
6
|
|
|
6
7
|
export interface LoadedMreactRouterViteConfig {
|
|
8
|
+
dehydrateOptions?: DehydrateOptions | undefined;
|
|
7
9
|
importPolicy?: AppRouterImportPolicy | undefined;
|
|
8
10
|
project: ResolvedAppRouterProject;
|
|
9
11
|
serverPort?: number | undefined;
|
|
@@ -46,8 +48,12 @@ export async function loadMreactRouterViteConfigDetails(options: {
|
|
|
46
48
|
const importPolicy = (config as ResolvedAppRouterProject & {
|
|
47
49
|
importPolicy?: AppRouterImportPolicy | undefined;
|
|
48
50
|
}).importPolicy;
|
|
51
|
+
const dehydrateOptions = (config as ResolvedAppRouterProject & {
|
|
52
|
+
dehydrateOptions?: DehydrateOptions | undefined;
|
|
53
|
+
}).dehydrateOptions;
|
|
49
54
|
|
|
50
55
|
return {
|
|
56
|
+
...(dehydrateOptions === undefined ? {} : { dehydrateOptions }),
|
|
51
57
|
...(importPolicy === undefined ? {} : { importPolicy }),
|
|
52
58
|
project: config,
|
|
53
59
|
...(typeof serverPort === "number" ? { serverPort } : {}),
|