@bleedingdev/modern-js-runtime 3.9.0-ultramodern.9 → 3.9.3-ultramodern.10
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/dist/cjs/cli/code.js +2 -2
- package/dist/cjs/cli/index.js +0 -7
- package/dist/cjs/cli/ssr/loadable-bundler-plugin.js +2 -0
- package/dist/cjs/cli/template.server.js +1 -2
- package/dist/cjs/core/browser/hydrate.js +3 -9
- package/dist/cjs/core/browser/index.js +4 -5
- package/dist/cjs/core/context/index.js +8 -2
- package/dist/cjs/core/context/public.js +4 -4
- package/dist/cjs/core/context/runtime.js +3 -9
- package/dist/cjs/core/index.js +3 -3
- package/dist/cjs/core/plugin/index.js +2 -2
- package/dist/cjs/core/react/index.js +3 -3
- package/dist/cjs/core/react/wrapper.js +7 -8
- package/dist/cjs/core/server/requestHandler.js +253 -25
- package/dist/cjs/core/server/stream/beforeTemplate.js +3 -3
- package/dist/cjs/core/server/stream/beforeTemplate.worker.js +2 -2
- package/dist/cjs/core/server/stream/createReadableStream.js +51 -19
- package/dist/cjs/core/server/stream/createReadableStream.worker.js +54 -27
- package/dist/cjs/core/server/stream/deferredScript.js +12 -6
- package/dist/cjs/core/server/stream/deferredScriptOutputCoordinator.js +370 -0
- package/dist/cjs/core/server/stream/shared.js +4 -0
- package/dist/cjs/core/server/string/index.js +6 -4
- package/dist/cjs/core/server/string/loadable.js +4 -4
- package/dist/cjs/document/cli/index.js +14 -5
- package/dist/cjs/exports/head.js +2 -2
- package/dist/cjs/internal.js +2 -2
- package/dist/cjs/router/cli/code/nestedRoutes.js +1 -2
- package/dist/cjs/router/cli/code/templates.js +2 -2
- package/dist/cjs/router/cli/code/utils.js +5 -4
- package/dist/cjs/router/runtime/DeferredDataScripts.node.js +13 -10
- package/dist/cjs/router/runtime/PrefetchLink.js +4 -4
- package/dist/cjs/router/runtime/hooks.js +6 -7
- package/dist/cjs/router/runtime/plugin.js +22 -27
- package/dist/cjs/router/runtime/plugin.node.js +8 -9
- package/dist/esm/cli/code.mjs +2 -2
- package/dist/esm/cli/index.mjs +0 -7
- package/dist/esm/cli/ssr/loadable-bundler-plugin.mjs +2 -0
- package/dist/esm/cli/template.server.mjs +1 -2
- package/dist/esm/core/browser/hydrate.mjs +3 -9
- package/dist/esm/core/browser/index.mjs +1 -2
- package/dist/esm/core/context/index.mjs +1 -1
- package/dist/esm/core/context/public.mjs +1 -1
- package/dist/esm/core/context/runtime.mjs +3 -9
- package/dist/esm/core/index.mjs +1 -1
- package/dist/esm/core/plugin/index.mjs +1 -1
- package/dist/esm/core/react/index.mjs +1 -1
- package/dist/esm/core/react/wrapper.mjs +1 -2
- package/dist/esm/core/server/requestHandler.mjs +223 -19
- package/dist/esm/core/server/stream/beforeTemplate.mjs +3 -3
- package/dist/esm/core/server/stream/beforeTemplate.worker.mjs +2 -2
- package/dist/esm/core/server/stream/createReadableStream.mjs +48 -16
- package/dist/esm/core/server/stream/createReadableStream.worker.mjs +53 -26
- package/dist/esm/core/server/stream/deferredScript.mjs +12 -6
- package/dist/esm/core/server/stream/deferredScriptOutputCoordinator.mjs +332 -0
- package/dist/esm/core/server/stream/shared.mjs +2 -1
- package/dist/esm/core/server/string/index.mjs +3 -1
- package/dist/esm/core/server/string/loadable.mjs +4 -4
- package/dist/esm/document/cli/index.mjs +12 -6
- package/dist/esm/exports/head.mjs +1 -1
- package/dist/esm/internal.mjs +1 -1
- package/dist/esm/router/cli/code/nestedRoutes.mjs +1 -2
- package/dist/esm/router/cli/code/templates.mjs +2 -2
- package/dist/esm/router/cli/code/utils.mjs +6 -5
- package/dist/esm/router/runtime/DeferredDataScripts.node.mjs +13 -10
- package/dist/esm/router/runtime/PrefetchLink.mjs +1 -1
- package/dist/esm/router/runtime/hooks.mjs +6 -7
- package/dist/esm/router/runtime/plugin.mjs +14 -19
- package/dist/esm/router/runtime/plugin.node.mjs +3 -4
- package/dist/esm-node/cli/code.mjs +2 -2
- package/dist/esm-node/cli/index.mjs +1 -9
- package/dist/esm-node/cli/ssr/loadable-bundler-plugin.mjs +2 -0
- package/dist/esm-node/cli/template.server.mjs +1 -2
- package/dist/esm-node/core/browser/hydrate.mjs +3 -9
- package/dist/esm-node/core/browser/index.mjs +1 -2
- package/dist/esm-node/core/context/index.mjs +1 -1
- package/dist/esm-node/core/context/public.mjs +1 -1
- package/dist/esm-node/core/context/runtime.mjs +3 -9
- package/dist/esm-node/core/index.mjs +1 -1
- package/dist/esm-node/core/plugin/index.mjs +1 -1
- package/dist/esm-node/core/react/index.mjs +1 -1
- package/dist/esm-node/core/react/wrapper.mjs +1 -2
- package/dist/esm-node/core/server/requestHandler.mjs +223 -19
- package/dist/esm-node/core/server/stream/beforeTemplate.mjs +3 -3
- package/dist/esm-node/core/server/stream/beforeTemplate.worker.mjs +2 -2
- package/dist/esm-node/core/server/stream/createReadableStream.mjs +48 -16
- package/dist/esm-node/core/server/stream/createReadableStream.worker.mjs +53 -26
- package/dist/esm-node/core/server/stream/deferredScript.mjs +12 -6
- package/dist/esm-node/core/server/stream/deferredScriptOutputCoordinator.mjs +333 -0
- package/dist/esm-node/core/server/stream/shared.mjs +2 -1
- package/dist/esm-node/core/server/string/index.mjs +3 -1
- package/dist/esm-node/core/server/string/loadable.mjs +4 -4
- package/dist/esm-node/document/cli/index.mjs +12 -6
- package/dist/esm-node/exports/head.mjs +1 -1
- package/dist/esm-node/internal.mjs +1 -1
- package/dist/esm-node/router/cli/code/nestedRoutes.mjs +1 -2
- package/dist/esm-node/router/cli/code/templates.mjs +2 -2
- package/dist/esm-node/router/cli/code/utils.mjs +6 -5
- package/dist/esm-node/router/runtime/DeferredDataScripts.node.mjs +13 -10
- package/dist/esm-node/router/runtime/PrefetchLink.mjs +1 -1
- package/dist/esm-node/router/runtime/hooks.mjs +6 -7
- package/dist/esm-node/router/runtime/plugin.mjs +14 -19
- package/dist/esm-node/router/runtime/plugin.node.mjs +3 -4
- package/dist/types/common.d.ts +2 -8
- package/dist/types/core/context/index.d.ts +1 -1
- package/dist/types/core/context/public.d.ts +1 -1
- package/dist/types/core/index.d.ts +1 -1
- package/dist/types/core/react/wrapper.d.ts +1 -1
- package/dist/types/core/server/requestHandler.d.ts +30 -1
- package/dist/types/core/server/stream/deferredScript.d.ts +1 -1
- package/dist/types/core/server/stream/deferredScriptOutputCoordinator.d.ts +72 -0
- package/dist/types/core/server/stream/shared.d.ts +14 -0
- package/dist/types/document/cli/index.d.ts +1 -0
- package/dist/types/internal.d.ts +1 -1
- package/dist/types/router/cli/code/utils.d.ts +1 -1
- package/dist/types/router/runtime/hooks.d.ts +12 -16
- package/package.json +34 -29
- package/rslib.config.mts +3 -0
- package/rstest.config.mts +0 -4
- package/dist/cjs/core/server/requestResponse.js +0 -107
- package/dist/cjs/core/server/routerCleanup.js +0 -129
- package/dist/cjs/rsc/server.worker.js +0 -62
- package/dist/esm/core/server/requestResponse.mjs +0 -63
- package/dist/esm/core/server/routerCleanup.mjs +0 -82
- package/dist/esm/rsc/server.worker.mjs +0 -1
- package/dist/esm-node/core/server/requestResponse.mjs +0 -64
- package/dist/esm-node/core/server/routerCleanup.mjs +0 -83
- package/dist/esm-node/rsc/server.worker.mjs +0 -2
- package/dist/types/core/server/requestResponse.d.ts +0 -15
- package/dist/types/core/server/routerCleanup.d.ts +0 -31
- package/dist/types/rsc/server.worker.d.ts +0 -1
|
@@ -1,16 +1,196 @@
|
|
|
1
1
|
import "node:module";
|
|
2
2
|
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import { getGlobalInternalRuntimeContext, getGlobalRSCRoot, getInitialContext } from "@modern-js/runtime/context";
|
|
3
4
|
import { storage } from "@modern-js/runtime-utils/node";
|
|
4
5
|
import { getPathname, parseCookie, parseHeaders, parseQuery } from "@modern-js/runtime-utils/universal/request";
|
|
5
6
|
import react, { Fragment } from "react";
|
|
6
|
-
import {
|
|
7
|
-
import { getInitialContext } from "../context/runtime.mjs";
|
|
7
|
+
import { handleRSCRedirect } from "../../router/runtime/redirect.mjs";
|
|
8
8
|
import { getServerPayload } from "../context/serverPayload/index.mjs";
|
|
9
9
|
import { createRoot } from "../react/index.mjs";
|
|
10
10
|
import { CHUNK_CSS_PLACEHOLDER } from "./constants.mjs";
|
|
11
|
-
import {
|
|
12
|
-
import { createRouterCleanup, finishWithRouterCleanup, runWithRouterCleanupOnError } from "./routerCleanup.mjs";
|
|
11
|
+
import { SSRErrors } from "./tracer.mjs";
|
|
13
12
|
import { getSSRConfigByEntry, getSSRMode } from "./utils.mjs";
|
|
13
|
+
const REQUEST_END_ERROR = 'An error occurs during request lifecycle completion';
|
|
14
|
+
const RESPONSE_BODY_CANCEL_ERROR = 'An error occurs while cancelling a discarded response body';
|
|
15
|
+
async function runWithRequestLifecycleOnError(lifecycle, callback) {
|
|
16
|
+
try {
|
|
17
|
+
return await callback();
|
|
18
|
+
} catch (error) {
|
|
19
|
+
await lifecycle.run({
|
|
20
|
+
status: 'error',
|
|
21
|
+
error
|
|
22
|
+
});
|
|
23
|
+
throw error;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
async function finishWithRequestLifecycle(lifecycle, callback) {
|
|
27
|
+
try {
|
|
28
|
+
return await callback();
|
|
29
|
+
} catch (error) {
|
|
30
|
+
if (!lifecycle.deferred) await lifecycle.run({
|
|
31
|
+
status: 'error',
|
|
32
|
+
error
|
|
33
|
+
});
|
|
34
|
+
throw error;
|
|
35
|
+
} finally{
|
|
36
|
+
if (!lifecycle.deferred) await lifecycle.run();
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function createRequestLifecycle(onEnd, onError) {
|
|
40
|
+
let deferred = false;
|
|
41
|
+
let finished;
|
|
42
|
+
const run = (terminal = {
|
|
43
|
+
status: 'complete'
|
|
44
|
+
})=>{
|
|
45
|
+
finished ??= Promise.resolve().then(()=>onEnd(terminal)).catch((error)=>{
|
|
46
|
+
onError(error, REQUEST_END_ERROR);
|
|
47
|
+
});
|
|
48
|
+
return finished;
|
|
49
|
+
};
|
|
50
|
+
const deferUntilBodyDone = (response)=>{
|
|
51
|
+
const { body } = response;
|
|
52
|
+
if (!body) return response;
|
|
53
|
+
deferred = true;
|
|
54
|
+
if (body.locked) throw new TypeError('Cannot observe a locked response body before request completion');
|
|
55
|
+
let cancelled = false;
|
|
56
|
+
const reader = body.getReader();
|
|
57
|
+
const wrappedBody = new ReadableStream({
|
|
58
|
+
async pull (controller) {
|
|
59
|
+
let result;
|
|
60
|
+
try {
|
|
61
|
+
result = await reader.read();
|
|
62
|
+
} catch (error) {
|
|
63
|
+
if (!cancelled) {
|
|
64
|
+
await run({
|
|
65
|
+
status: 'error',
|
|
66
|
+
error
|
|
67
|
+
});
|
|
68
|
+
controller.error(error);
|
|
69
|
+
}
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
if (cancelled) return;
|
|
73
|
+
if (result.done) {
|
|
74
|
+
await run();
|
|
75
|
+
if (!cancelled) controller.close();
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
controller.enqueue(result.value);
|
|
79
|
+
},
|
|
80
|
+
async cancel (reason) {
|
|
81
|
+
cancelled = true;
|
|
82
|
+
try {
|
|
83
|
+
await reader.cancel(reason);
|
|
84
|
+
} catch {}
|
|
85
|
+
await run({
|
|
86
|
+
status: 'cancelled',
|
|
87
|
+
reason
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
return new Response(wrappedBody, {
|
|
92
|
+
status: response.status,
|
|
93
|
+
statusText: response.statusText,
|
|
94
|
+
headers: response.headers
|
|
95
|
+
});
|
|
96
|
+
};
|
|
97
|
+
const discardBody = async (response, terminal = {
|
|
98
|
+
status: 'discarded'
|
|
99
|
+
})=>{
|
|
100
|
+
const { body } = response;
|
|
101
|
+
if (!body) return void await run(terminal);
|
|
102
|
+
deferred = true;
|
|
103
|
+
if (body.locked) throw new TypeError('Cannot discard a locked response body before request completion');
|
|
104
|
+
try {
|
|
105
|
+
await body.cancel('Response body discarded during finalization');
|
|
106
|
+
} catch (error) {
|
|
107
|
+
await run('error' === terminal.status ? terminal : {
|
|
108
|
+
status: 'error',
|
|
109
|
+
error
|
|
110
|
+
});
|
|
111
|
+
if ('error' === terminal.status) onError(error, RESPONSE_BODY_CANCEL_ERROR);
|
|
112
|
+
throw error;
|
|
113
|
+
}
|
|
114
|
+
await run(terminal);
|
|
115
|
+
};
|
|
116
|
+
return {
|
|
117
|
+
get deferred () {
|
|
118
|
+
return deferred;
|
|
119
|
+
},
|
|
120
|
+
run,
|
|
121
|
+
deferUntilBodyDone,
|
|
122
|
+
discardBody
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
const isRedirectStatus = (status)=>301 === status || 302 === status || 303 === status || 307 === status || 308 === status;
|
|
126
|
+
const isNullBodyStatus = (status)=>204 === status || 205 === status || 304 === status;
|
|
127
|
+
const getRedirectLocation = (headers)=>{
|
|
128
|
+
const location = headers.get('Location');
|
|
129
|
+
return null !== location && '' !== location && URL.canParse(location, 'http://localhost') ? location : void 0;
|
|
130
|
+
};
|
|
131
|
+
const processRedirect = (headers, status, ctx)=>{
|
|
132
|
+
headers.delete('content-length');
|
|
133
|
+
headers.delete('transfer-encoding');
|
|
134
|
+
if (ctx.enableRsc && ctx.isRSCNavigation) return handleRSCRedirect(headers, ctx.basename, status);
|
|
135
|
+
return new Response(null, {
|
|
136
|
+
status,
|
|
137
|
+
headers
|
|
138
|
+
});
|
|
139
|
+
};
|
|
140
|
+
const applyRouterResult = (context, routerResult, onError)=>{
|
|
141
|
+
const routerStatusCode = routerResult?.statusCode;
|
|
142
|
+
if (void 0 !== routerStatusCode && 0 !== routerStatusCode && !Number.isNaN(routerStatusCode) && 200 !== routerStatusCode) context.ssrContext?.response.status(routerStatusCode);
|
|
143
|
+
const errors = Object.values(routerResult?.errors || {});
|
|
144
|
+
if (errors.length > 0) onError(errors[0], SSRErrors.LOADER_ERROR);
|
|
145
|
+
};
|
|
146
|
+
const createLoaderRedirectResponse = (beforeRenderResult, redirectCtx)=>{
|
|
147
|
+
if (void 0 === beforeRenderResult || !isRedirectStatus(beforeRenderResult.status)) return;
|
|
148
|
+
if (beforeRenderResult.headers.has('X-Modernjs-Redirect')) return beforeRenderResult;
|
|
149
|
+
const redirectUrl = getRedirectLocation(beforeRenderResult.headers);
|
|
150
|
+
if (void 0 === redirectUrl) return;
|
|
151
|
+
return processRedirect(new Headers(beforeRenderResult.headers), beforeRenderResult.status, redirectCtx);
|
|
152
|
+
};
|
|
153
|
+
const finalizeRenderResponse = async (response, responseProxy, redirectCtx, lifecycle)=>{
|
|
154
|
+
try {
|
|
155
|
+
const proxyHeaders = new Headers(responseProxy.headers);
|
|
156
|
+
if (-1 !== responseProxy.status && isRedirectStatus(responseProxy.status) && void 0 !== getRedirectLocation(proxyHeaders)) {
|
|
157
|
+
await lifecycle.discardBody(response);
|
|
158
|
+
return processRedirect(proxyHeaders, responseProxy.status, redirectCtx);
|
|
159
|
+
}
|
|
160
|
+
const headers = new Headers(response.headers);
|
|
161
|
+
Object.entries(responseProxy.headers).forEach(([key, value])=>{
|
|
162
|
+
headers.set(key, value);
|
|
163
|
+
});
|
|
164
|
+
if (-1 !== responseProxy.status) {
|
|
165
|
+
if (isNullBodyStatus(responseProxy.status)) {
|
|
166
|
+
await lifecycle.discardBody(response);
|
|
167
|
+
headers.delete('content-length');
|
|
168
|
+
headers.delete('transfer-encoding');
|
|
169
|
+
return new Response(null, {
|
|
170
|
+
status: responseProxy.status,
|
|
171
|
+
headers
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
if (response.body?.locked) lifecycle.deferUntilBodyDone(response);
|
|
175
|
+
return lifecycle.deferUntilBodyDone(new Response(response.body, {
|
|
176
|
+
status: responseProxy.status,
|
|
177
|
+
headers
|
|
178
|
+
}));
|
|
179
|
+
}
|
|
180
|
+
Object.entries(responseProxy.headers).forEach(([key, value])=>{
|
|
181
|
+
response.headers.set(key, value);
|
|
182
|
+
});
|
|
183
|
+
return lifecycle.deferUntilBodyDone(response);
|
|
184
|
+
} catch (error) {
|
|
185
|
+
if (!lifecycle.deferred) try {
|
|
186
|
+
await lifecycle.discardBody(response, {
|
|
187
|
+
status: 'error',
|
|
188
|
+
error
|
|
189
|
+
});
|
|
190
|
+
} catch {}
|
|
191
|
+
throw error;
|
|
192
|
+
}
|
|
193
|
+
};
|
|
14
194
|
async function handleRSCRequest(request, Root, context, options, handleRequest) {
|
|
15
195
|
const serverPayload = getServerPayload();
|
|
16
196
|
if (void 0 !== serverPayload) return await handleRequest(request, Root, {
|
|
@@ -101,12 +281,14 @@ const createRequestHandler = async (handleRequest, createRequestOptions)=>{
|
|
|
101
281
|
status: -1
|
|
102
282
|
};
|
|
103
283
|
const activeDeferreds = new Map();
|
|
284
|
+
const deferredScriptKeys = new Map();
|
|
104
285
|
return storage.run({
|
|
105
286
|
headers: headersData,
|
|
106
287
|
request,
|
|
107
288
|
monitors: options.monitors,
|
|
108
289
|
responseProxy,
|
|
109
290
|
activeDeferreds,
|
|
291
|
+
deferredScriptKeys,
|
|
110
292
|
serverPayload: void 0
|
|
111
293
|
}, async ()=>{
|
|
112
294
|
const Root = createRoot();
|
|
@@ -131,28 +313,50 @@ const createRequestHandler = async (handleRequest, createRequestOptions)=>{
|
|
|
131
313
|
isRSCNavigation: 'true' === request.headers.get('x-rsc-tree'),
|
|
132
314
|
basename: ssrContext.baseUrl || '/'
|
|
133
315
|
};
|
|
134
|
-
const
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
316
|
+
const lifecycle = createRequestLifecycle(async (terminal)=>{
|
|
317
|
+
await hooks.onRequestEnd?.call({
|
|
318
|
+
runtimeContext: context,
|
|
319
|
+
terminal
|
|
320
|
+
});
|
|
321
|
+
}, options.onError);
|
|
322
|
+
const beforeRenderResult = await runWithRequestLifecycleOnError(lifecycle, ()=>runBeforeRender(context));
|
|
323
|
+
let redirectResponse;
|
|
324
|
+
try {
|
|
325
|
+
const prepared = {
|
|
326
|
+
runtimeContext: context,
|
|
327
|
+
routerResult: context.routerContext
|
|
328
|
+
};
|
|
329
|
+
const result = await hooks.onRenderPrepared?.call(prepared) ?? prepared;
|
|
330
|
+
applyRouterResult(context, result.routerResult, options.onError);
|
|
331
|
+
if ("u" > typeof Response) redirectResponse = createLoaderRedirectResponse(beforeRenderResult, redirectCtx);
|
|
332
|
+
} catch (error) {
|
|
333
|
+
if (beforeRenderResult) try {
|
|
334
|
+
await lifecycle.discardBody(beforeRenderResult, {
|
|
335
|
+
status: 'error',
|
|
336
|
+
error
|
|
337
|
+
});
|
|
338
|
+
} catch {}
|
|
339
|
+
else await lifecycle.run({
|
|
340
|
+
status: 'error',
|
|
341
|
+
error
|
|
342
|
+
});
|
|
343
|
+
throw error;
|
|
344
|
+
}
|
|
345
|
+
if (redirectResponse) {
|
|
346
|
+
const response = redirectResponse;
|
|
347
|
+
if (beforeRenderResult?.body && response !== beforeRenderResult) await lifecycle.discardBody(beforeRenderResult);
|
|
348
|
+
return finishWithRequestLifecycle(lifecycle, ()=>lifecycle.deferUntilBodyDone(response));
|
|
145
349
|
}
|
|
146
|
-
await
|
|
350
|
+
await runWithRequestLifecycleOnError(lifecycle, ()=>{
|
|
147
351
|
if (!createRequestOptions?.enableRsc) {
|
|
148
352
|
const { htmlTemplate } = options.resource;
|
|
149
353
|
options.resource.htmlTemplate = htmlTemplate.replace('</head>', `${CHUNK_CSS_PLACEHOLDER}</head>`);
|
|
150
354
|
}
|
|
151
355
|
});
|
|
152
|
-
const response = await
|
|
153
|
-
return
|
|
356
|
+
const response = await runWithRequestLifecycleOnError(lifecycle, ()=>renderRequest(request, Root, context, options, handleRequest, !!createRequestOptions?.enableRsc));
|
|
357
|
+
return finishWithRequestLifecycle(lifecycle, ()=>finalizeRenderResponse(response, responseProxy, redirectCtx, lifecycle));
|
|
154
358
|
});
|
|
155
359
|
};
|
|
156
360
|
return requestHandler;
|
|
157
361
|
};
|
|
158
|
-
export { createRequestHandler };
|
|
362
|
+
export { REQUEST_END_ERROR, RESPONSE_BODY_CANCEL_ERROR, applyRouterResult, createLoaderRedirectResponse, createRequestHandler, createRequestLifecycle, finalizeRenderResponse, finishWithRequestLifecycle, runWithRequestLifecycleOnError };
|
|
@@ -15,8 +15,8 @@ const readAsset = async (chunk)=>{
|
|
|
15
15
|
};
|
|
16
16
|
const checkIsInline = (chunk, enableInline)=>{
|
|
17
17
|
if ('production' !== process.env.NODE_ENV) return false;
|
|
18
|
-
if (enableInline instanceof RegExp) return enableInline.test(chunk);
|
|
19
|
-
return Boolean(enableInline);
|
|
18
|
+
else if (enableInline instanceof RegExp) return enableInline.test(chunk);
|
|
19
|
+
else return Boolean(enableInline);
|
|
20
20
|
};
|
|
21
21
|
async function buildShellBeforeTemplate(beforeAppTemplate, options) {
|
|
22
22
|
const { config, runtimeContext, styledComponentsStyleTags, entryName, lifecycle, helmetData } = options;
|
|
@@ -74,7 +74,7 @@ async function buildShellBeforeTemplate(beforeAppTemplate, options) {
|
|
|
74
74
|
const styles = await Promise.all(cssChunks.map(async (chunk)=>{
|
|
75
75
|
const link = `<link href="${chunk}" rel="stylesheet" />`;
|
|
76
76
|
if (checkIsNode() && checkIsInline(chunk, inlineStyles)) return readAsset(chunk).then((content)=>`<style>${content}</style>`).catch((_)=>link);
|
|
77
|
-
return link;
|
|
77
|
+
else return link;
|
|
78
78
|
}));
|
|
79
79
|
return `${styles.join('')}`;
|
|
80
80
|
}
|
|
@@ -5,8 +5,8 @@ import { buildHtml, replaceSSRTemplateChunk } from "../shared.mjs";
|
|
|
5
5
|
import { hasStylesheetLink } from "../utils.mjs";
|
|
6
6
|
const checkIsInline = (chunk, enableInline)=>{
|
|
7
7
|
if ('production' !== process.env.NODE_ENV) return false;
|
|
8
|
-
if (enableInline instanceof RegExp) return enableInline.test(chunk);
|
|
9
|
-
return Boolean(enableInline);
|
|
8
|
+
else if (enableInline instanceof RegExp) return enableInline.test(chunk);
|
|
9
|
+
else return Boolean(enableInline);
|
|
10
10
|
};
|
|
11
11
|
async function buildShellBeforeTemplate(beforeAppTemplate, options) {
|
|
12
12
|
const { config, runtimeContext, styledComponentsStyleTags, entryName, lifecycle, helmetData } = options;
|
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
import "node:module";
|
|
2
|
+
import { getGlobalEnableRsc, getGlobalInternalRuntimeContext } from "@modern-js/runtime/context";
|
|
2
3
|
import { storage } from "@modern-js/runtime-utils/node";
|
|
3
4
|
import { SSR_HYDRATION_ID_PREFIX } from "@modern-js/utils/universal/constants";
|
|
4
5
|
import { PassThrough, Readable, Transform, finished, pipeline } from "stream";
|
|
6
|
+
import { StringDecoder } from "string_decoder";
|
|
5
7
|
import { ESCAPED_SHELL_STREAM_END_MARK } from "../../../common.mjs";
|
|
6
8
|
import { RenderLevel } from "../../constants.mjs";
|
|
7
|
-
import { getGlobalEnableRsc, getGlobalInternalRuntimeContext } from "../../context/index.mjs";
|
|
8
9
|
import { getMonitors } from "../../context/monitors/index.mjs";
|
|
9
10
|
import { wrapRuntimeComponentResolver } from "../../react/wrapper.mjs";
|
|
10
11
|
import { createReplaceHelemt, getHelmetData } from "../helmet.mjs";
|
|
11
12
|
import { createSSRRenderLifecycle, createSSRStreamErrorReporter, observeSSRStream, orderSSRStreamTransforms } from "../shared.mjs";
|
|
12
13
|
import { enqueueFromEntries } from "./deferredScript.mjs";
|
|
13
|
-
import {
|
|
14
|
+
import { DeferredScriptOutputCoordinator } from "./deferredScriptOutputCoordinator.mjs";
|
|
15
|
+
import { SHELL_PROGRESSIVE_CHUNK_SIZE, ShellChunkStatus, getReadableStreamFromString, resolveStreamingMode } from "./shared.mjs";
|
|
14
16
|
import { getTemplates } from "./template.mjs";
|
|
15
17
|
const createReadableStreamFromElement = async (request, rootElement, options)=>{
|
|
16
18
|
const { renderToPipeableStream } = await import("react-dom/server");
|
|
@@ -24,6 +26,7 @@ const createReadableStreamFromElement = async (request, rootElement, options)=>{
|
|
|
24
26
|
config,
|
|
25
27
|
platform: 'node',
|
|
26
28
|
mode: 'stream',
|
|
29
|
+
monitors: getMonitors(),
|
|
27
30
|
isRsc,
|
|
28
31
|
terminalMarker: ESCAPED_SHELL_STREAM_END_MARK
|
|
29
32
|
}) || [];
|
|
@@ -122,29 +125,42 @@ const createReadableStreamFromElement = async (request, rootElement, options)=>{
|
|
|
122
125
|
});
|
|
123
126
|
if (failed) return;
|
|
124
127
|
const chunks = [];
|
|
128
|
+
let bufferedLength = 0;
|
|
125
129
|
const marker = Buffer.from(ESCAPED_SHELL_STREAM_END_MARK);
|
|
130
|
+
let markerTail = Buffer.alloc(0);
|
|
131
|
+
const decoder = new StringDecoder('utf8');
|
|
126
132
|
const pendingScripts = [];
|
|
133
|
+
let deferredResolversComplete = Promise.resolve();
|
|
127
134
|
let shellChunkStatus = ShellChunkStatus.START;
|
|
128
|
-
const emitShell = (
|
|
135
|
+
const emitShell = (buffered, markerIndex)=>{
|
|
129
136
|
const beforeMark = lifecycle.completedBody(buffered.subarray(0, markerIndex).toString('utf8'), 'shell');
|
|
130
137
|
const completedShellBefore = createReplaceHelemt(getHelmetData(extenders))(shellBefore);
|
|
131
138
|
shellChunkStatus = ShellChunkStatus.FINISH;
|
|
132
139
|
chunks.length = 0;
|
|
133
|
-
|
|
140
|
+
bufferedLength = 0;
|
|
141
|
+
coordinator.writeReact(`${completedShellBefore}${beforeMark}${shellAfter}`);
|
|
134
142
|
const afterMark = buffered.subarray(markerIndex + marker.length);
|
|
135
|
-
if (afterMark.length > 0)
|
|
136
|
-
|
|
143
|
+
if (afterMark.length > 0) coordinator.writeReact(decoder.write(afterMark));
|
|
144
|
+
coordinator.markShellFinished();
|
|
145
|
+
for (const script of pendingScripts)coordinator.enqueueResolver(script);
|
|
137
146
|
pendingScripts.length = 0;
|
|
138
147
|
};
|
|
139
148
|
const body = new Transform({
|
|
140
149
|
transform (chunk, _encoding, callback) {
|
|
141
150
|
try {
|
|
142
|
-
|
|
151
|
+
const bytes = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
152
|
+
if (shellChunkStatus === ShellChunkStatus.FINISH) coordinator.writeReact(decoder.write(bytes));
|
|
143
153
|
else {
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
154
|
+
const window = Buffer.concat([
|
|
155
|
+
markerTail,
|
|
156
|
+
bytes
|
|
157
|
+
]);
|
|
158
|
+
const windowIndex = window.indexOf(marker);
|
|
159
|
+
const windowStart = bufferedLength - markerTail.length;
|
|
160
|
+
chunks.push(bytes);
|
|
161
|
+
bufferedLength += bytes.length;
|
|
162
|
+
if (-1 === windowIndex) markerTail = window.subarray(Math.max(0, window.length - marker.length + 1));
|
|
163
|
+
else emitShell(Buffer.concat(chunks, bufferedLength), windowStart + windowIndex);
|
|
148
164
|
}
|
|
149
165
|
callback();
|
|
150
166
|
} catch (error) {
|
|
@@ -155,14 +171,27 @@ const createReadableStreamFromElement = async (request, rootElement, options)=>{
|
|
|
155
171
|
try {
|
|
156
172
|
if (shellChunkStatus !== ShellChunkStatus.FINISH) {
|
|
157
173
|
const buffered = Buffer.concat(chunks);
|
|
158
|
-
emitShell(
|
|
174
|
+
emitShell(buffered, buffered.length);
|
|
159
175
|
}
|
|
160
|
-
|
|
176
|
+
const trailing = decoder.end();
|
|
177
|
+
if (trailing) coordinator.writeReact(trailing);
|
|
178
|
+
deferredResolversComplete.then(()=>{
|
|
179
|
+
try {
|
|
180
|
+
coordinator.finish();
|
|
181
|
+
callback();
|
|
182
|
+
} catch (error) {
|
|
183
|
+
callback(error instanceof Error ? error : new Error(String(error)));
|
|
184
|
+
}
|
|
185
|
+
}, (error)=>callback(error instanceof Error ? error : new Error(String(error))));
|
|
161
186
|
} catch (error) {
|
|
162
187
|
callback(error instanceof Error ? error : new Error(String(error)));
|
|
163
188
|
}
|
|
164
189
|
}
|
|
165
190
|
});
|
|
191
|
+
const coordinator = new DeferredScriptOutputCoordinator((content)=>{
|
|
192
|
+
body.push(content);
|
|
193
|
+
});
|
|
194
|
+
body.once('close', ()=>coordinator.abort());
|
|
166
195
|
own(body);
|
|
167
196
|
const passThrough = new PassThrough();
|
|
168
197
|
let processedStream = own(passThrough);
|
|
@@ -173,11 +202,13 @@ const createReadableStreamFromElement = async (request, rootElement, options)=>{
|
|
|
173
202
|
deliver(Readable.toWeb(body));
|
|
174
203
|
reactStream.pipe(passThrough);
|
|
175
204
|
try {
|
|
176
|
-
const
|
|
205
|
+
const storageContext = storage.useContext?.();
|
|
206
|
+
const activeDeferreds = storageContext?.activeDeferreds;
|
|
207
|
+
const deferredScriptKeys = storageContext?.deferredScriptKeys;
|
|
177
208
|
const entries = activeDeferreds instanceof Map ? Array.from(activeDeferreds.entries()) : [];
|
|
178
|
-
enqueueFromEntries(entries, config.nonce, (script)=>{
|
|
209
|
+
if (entries.length > 0) deferredResolversComplete = enqueueFromEntries(entries, deferredScriptKeys, config.nonce, (script)=>{
|
|
179
210
|
if (failed || body.destroyed || body.writableEnded) return;
|
|
180
|
-
if (shellChunkStatus === ShellChunkStatus.FINISH)
|
|
211
|
+
if (shellChunkStatus === ShellChunkStatus.FINISH) coordinator.enqueueResolver(script);
|
|
181
212
|
else pendingScripts.push(script);
|
|
182
213
|
});
|
|
183
214
|
} catch (error) {
|
|
@@ -214,6 +245,7 @@ const createReadableStreamFromElement = async (request, rootElement, options)=>{
|
|
|
214
245
|
reactStream = renderToPipeableStream(processedRootElement, {
|
|
215
246
|
nonce: config.nonce,
|
|
216
247
|
identifierPrefix: SSR_HYDRATION_ID_PREFIX,
|
|
248
|
+
progressiveChunkSize: SHELL_PROGRESSIVE_CHUNK_SIZE,
|
|
217
249
|
[onReady] () {
|
|
218
250
|
startOutput().catch(fail);
|
|
219
251
|
},
|
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
import "node:module";
|
|
2
2
|
import { renderSSRStream } from "@modern-js/render/ssr";
|
|
3
|
+
import { getGlobalEnableRsc, getGlobalInternalRuntimeContext } from "@modern-js/runtime/context";
|
|
3
4
|
import { storage } from "@modern-js/runtime-utils/node";
|
|
4
5
|
import react from "react";
|
|
5
6
|
import { ESCAPED_SHELL_STREAM_END_MARK } from "../../../common.mjs";
|
|
6
7
|
import { RenderLevel } from "../../constants.mjs";
|
|
7
|
-
import {
|
|
8
|
+
import { getMonitors } from "../../context/monitors/index.mjs";
|
|
8
9
|
import { wrapRuntimeComponentResolver } from "../../react/wrapper.mjs";
|
|
9
10
|
import { createReplaceHelemt, getHelmetData } from "../helmet.mjs";
|
|
10
11
|
import { createSSRRenderLifecycle, createSSRStreamErrorReporter, observeSSRStream, orderSSRStreamTransforms } from "../shared.mjs";
|
|
11
12
|
import { enqueueFromEntries } from "./deferredScript.mjs";
|
|
12
|
-
import {
|
|
13
|
+
import { DeferredScriptOutputCoordinator } from "./deferredScriptOutputCoordinator.mjs";
|
|
14
|
+
import { SHELL_PROGRESSIVE_CHUNK_SIZE, ShellChunkStatus, getReadableStreamFromString, resolveStreamingMode } from "./shared.mjs";
|
|
13
15
|
import { getTemplates } from "./template.mjs";
|
|
14
16
|
const createReadableStreamFromElement = async (request, rootElement, options)=>{
|
|
15
17
|
const { htmlTemplate, runtimeContext, config, ssrConfig, entryName, resource, rscManifest, rscRoot } = options;
|
|
@@ -22,6 +24,7 @@ const createReadableStreamFromElement = async (request, rootElement, options)=>{
|
|
|
22
24
|
config,
|
|
23
25
|
platform: 'web',
|
|
24
26
|
mode: 'stream',
|
|
27
|
+
monitors: getMonitors(),
|
|
25
28
|
isRsc,
|
|
26
29
|
terminalMarker: ESCAPED_SHELL_STREAM_END_MARK
|
|
27
30
|
}) || [];
|
|
@@ -34,6 +37,7 @@ const createReadableStreamFromElement = async (request, rootElement, options)=>{
|
|
|
34
37
|
let pendingSource;
|
|
35
38
|
let bodyOpen = true;
|
|
36
39
|
let bodyPipe;
|
|
40
|
+
let coordinator;
|
|
37
41
|
const templateOptions = {
|
|
38
42
|
runtimeContext,
|
|
39
43
|
ssrConfig,
|
|
@@ -52,6 +56,7 @@ const createReadableStreamFromElement = async (request, rootElement, options)=>{
|
|
|
52
56
|
request,
|
|
53
57
|
signal: request.signal,
|
|
54
58
|
nonce: config.nonce,
|
|
59
|
+
progressiveChunkSize: SHELL_PROGRESSIVE_CHUNK_SIZE,
|
|
55
60
|
rscManifest,
|
|
56
61
|
rscRoot: rscRoot,
|
|
57
62
|
routes: runtimeContext.routes,
|
|
@@ -80,66 +85,87 @@ const createReadableStreamFromElement = async (request, rootElement, options)=>{
|
|
|
80
85
|
const decoder = new TextDecoder();
|
|
81
86
|
const encoder = new TextEncoder();
|
|
82
87
|
const pendingScripts = [];
|
|
83
|
-
|
|
88
|
+
const buffered = [];
|
|
89
|
+
let bufferedLength = 0;
|
|
90
|
+
let markerTail = '';
|
|
84
91
|
let shellChunkStatus = ShellChunkStatus.START;
|
|
85
92
|
let bodyController;
|
|
86
|
-
|
|
93
|
+
let deferredResolversComplete = Promise.resolve();
|
|
94
|
+
coordinator = new DeferredScriptOutputCoordinator((content)=>{
|
|
95
|
+
if (!bodyOpen) return;
|
|
96
|
+
try {
|
|
97
|
+
bodyController.enqueue(encoder.encode(content));
|
|
98
|
+
} catch {
|
|
99
|
+
bodyOpen = false;
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
const emit = (chunk)=>{
|
|
87
103
|
if (shellChunkStatus === ShellChunkStatus.FINISH) {
|
|
88
|
-
if (chunk.length > 0)
|
|
104
|
+
if (chunk.length > 0) coordinator?.writeReact(chunk);
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
const window = markerTail + chunk;
|
|
108
|
+
const windowIndex = window.indexOf(ESCAPED_SHELL_STREAM_END_MARK);
|
|
109
|
+
const markerIndex = bufferedLength - markerTail.length + windowIndex;
|
|
110
|
+
buffered.push(chunk);
|
|
111
|
+
bufferedLength += chunk.length;
|
|
112
|
+
if (-1 === windowIndex) {
|
|
113
|
+
markerTail = window.slice(Math.max(0, window.length - ESCAPED_SHELL_STREAM_END_MARK.length + 1));
|
|
89
114
|
return;
|
|
90
115
|
}
|
|
91
|
-
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
-
const beforeMark = lifecycle.completedBody(buffered.slice(0, markerIndex), 'shell');
|
|
95
|
-
const afterMark = buffered.slice(markerIndex + ESCAPED_SHELL_STREAM_END_MARK.length);
|
|
116
|
+
const shell = buffered.join('');
|
|
117
|
+
const beforeMark = lifecycle.completedBody(shell.slice(0, markerIndex), 'shell');
|
|
118
|
+
const afterMark = shell.slice(markerIndex + ESCAPED_SHELL_STREAM_END_MARK.length);
|
|
96
119
|
const completedShellBefore = createReplaceHelemt(getHelmetData(extenders))(shellBefore);
|
|
97
120
|
shellChunkStatus = ShellChunkStatus.FINISH;
|
|
98
|
-
buffered =
|
|
99
|
-
|
|
100
|
-
if (afterMark.length > 0)
|
|
101
|
-
|
|
121
|
+
buffered.length = 0;
|
|
122
|
+
coordinator?.writeReact(`${completedShellBefore}${beforeMark}${shellAfter}`);
|
|
123
|
+
if (afterMark.length > 0) coordinator?.writeReact(afterMark);
|
|
124
|
+
coordinator?.markShellFinished();
|
|
125
|
+
for (const script of pendingScripts)coordinator?.enqueueResolver(script);
|
|
102
126
|
pendingScripts.length = 0;
|
|
103
127
|
};
|
|
104
128
|
const body = new TransformStream({
|
|
105
129
|
start (controller) {
|
|
106
130
|
bodyController = controller;
|
|
107
131
|
},
|
|
108
|
-
transform (chunk
|
|
132
|
+
transform (chunk) {
|
|
109
133
|
emit(decoder.decode(chunk, {
|
|
110
134
|
stream: true
|
|
111
|
-
})
|
|
135
|
+
}));
|
|
112
136
|
},
|
|
113
|
-
flush (
|
|
114
|
-
|
|
115
|
-
emit(decoder.decode(), controller);
|
|
137
|
+
async flush () {
|
|
138
|
+
emit(decoder.decode());
|
|
116
139
|
if (shellChunkStatus !== ShellChunkStatus.FINISH) throw new Error('React SSR stream ended before the shell marker');
|
|
140
|
+
await deferredResolversComplete;
|
|
141
|
+
coordinator?.finish();
|
|
142
|
+
bodyOpen = false;
|
|
117
143
|
}
|
|
118
144
|
});
|
|
119
145
|
bodyPipe = pendingSource.pipeTo(body.writable);
|
|
120
146
|
bodyPipe.catch(()=>{});
|
|
121
147
|
pendingSource = body.readable;
|
|
122
|
-
const
|
|
148
|
+
const storageContext = storage.useContext?.();
|
|
149
|
+
const activeDeferreds = storageContext?.activeDeferreds;
|
|
150
|
+
const deferredScriptKeys = storageContext?.deferredScriptKeys;
|
|
123
151
|
const entries = activeDeferreds instanceof Map ? Array.from(activeDeferreds.entries()) : [];
|
|
124
|
-
enqueueFromEntries(entries, config.nonce, (script)=>{
|
|
152
|
+
if (entries.length > 0) deferredResolversComplete = enqueueFromEntries(entries, deferredScriptKeys, config.nonce, (script)=>{
|
|
125
153
|
if (!bodyOpen) return;
|
|
126
|
-
if (shellChunkStatus === ShellChunkStatus.FINISH)
|
|
127
|
-
bodyController.enqueue(encoder.encode(script));
|
|
128
|
-
} catch {
|
|
129
|
-
bodyOpen = false;
|
|
130
|
-
}
|
|
154
|
+
if (shellChunkStatus === ShellChunkStatus.FINISH) coordinator?.enqueueResolver(script);
|
|
131
155
|
else pendingScripts.push(script);
|
|
132
156
|
});
|
|
133
157
|
return observeSSRStream(pendingSource, {
|
|
134
158
|
lifecycle,
|
|
135
159
|
async onError (error) {
|
|
136
160
|
bodyOpen = false;
|
|
161
|
+
coordinator?.abort();
|
|
137
162
|
await bodyPipe?.catch(()=>{});
|
|
138
163
|
reportError(error);
|
|
139
164
|
},
|
|
140
165
|
signal: request.signal,
|
|
141
166
|
async onCancel (reason) {
|
|
142
167
|
bodyOpen = false;
|
|
168
|
+
coordinator?.abort();
|
|
143
169
|
await bodyPipe?.catch((error)=>{
|
|
144
170
|
if (error !== reason) throw error;
|
|
145
171
|
});
|
|
@@ -148,6 +174,7 @@ const createReadableStreamFromElement = async (request, rootElement, options)=>{
|
|
|
148
174
|
});
|
|
149
175
|
} catch (error) {
|
|
150
176
|
bodyOpen = false;
|
|
177
|
+
coordinator?.abort();
|
|
151
178
|
if (void 0 !== pendingSource) try {
|
|
152
179
|
await pendingSource.cancel(error);
|
|
153
180
|
await bodyPipe?.catch(()=>{});
|
|
@@ -45,23 +45,29 @@ function buildDeferredDataScript(nonce, args) {
|
|
|
45
45
|
const nonceAttr = void 0 !== nonce && '' !== nonce ? ` nonce="${nonce}"` : '';
|
|
46
46
|
return `<script async${nonceAttr} data-fn-name="r" data-script-src="modern-run-router-data-fn" data-fn-args='${escaped}' suppressHydrationWarning>${runRouterDataFnStr}</script>`;
|
|
47
47
|
}
|
|
48
|
-
function enqueueFromEntries(entries, nonce, emit) {
|
|
48
|
+
function enqueueFromEntries(entries, deferredScriptKeys, nonce, emit) {
|
|
49
|
+
const pendingResolvers = [];
|
|
49
50
|
entries.forEach(([routeId, value])=>{
|
|
50
51
|
if (!isDeferredDataLike(value)) return;
|
|
51
|
-
const
|
|
52
|
-
|
|
52
|
+
const serializedKeys = void 0 !== deferredScriptKeys ? deferredScriptKeys.get(routeId) ?? [] : value.pendingKeys ?? [];
|
|
53
|
+
serializedKeys.forEach((key)=>{
|
|
53
54
|
const tracked = value.data?.[key];
|
|
54
|
-
if (isPromiseLike(tracked)) tracked.then((val)=>
|
|
55
|
+
if (isPromiseLike(tracked)) pendingResolvers.push(Promise.resolve(tracked).then((val)=>{
|
|
56
|
+
emit(buildDeferredDataScript(nonce, [
|
|
55
57
|
routeId,
|
|
56
58
|
key,
|
|
57
59
|
val
|
|
58
|
-
]))
|
|
60
|
+
]));
|
|
61
|
+
}, (err)=>{
|
|
62
|
+
emit(buildDeferredDataScript(nonce, [
|
|
59
63
|
routeId,
|
|
60
64
|
key,
|
|
61
65
|
void 0,
|
|
62
66
|
toErrorInfo(err)
|
|
63
|
-
]))
|
|
67
|
+
]));
|
|
68
|
+
}));
|
|
64
69
|
});
|
|
65
70
|
});
|
|
71
|
+
return Promise.all(pendingResolvers).then(()=>void 0);
|
|
66
72
|
}
|
|
67
73
|
export { buildDeferredDataScript, enqueueFromEntries, escapeAttr, isDeferredDataLike, isPromiseLike, isRecord, toErrorInfo };
|