@bleedingdev/modern-js-federation-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/module-federation/consume-surface.js +6 -16
- package/dist/cjs/module-federation/distributed-ssr.js +6 -1
- package/dist/cjs/module-federation/index.js +13 -239
- package/dist/cjs/module-federation/manifest-recovery-runtime-plugin.js +1 -1
- package/dist/cjs/module-federation/native-browser-dev-client.js +320 -0
- package/dist/cjs/module-federation/native-browser-dev-refresh.js +226 -0
- package/dist/cjs/module-federation/native-browser-dev-runtime-plugin.js +233 -0
- package/dist/cjs/module-federation/native-dev-refresh-runtime-plugin.js +267 -0
- package/dist/cjs/module-federation/renderer-contract.js +159 -0
- package/dist/cjs/module-federation/renderer-runtime-plugin.js +268 -0
- package/dist/cjs/module-federation/telemetry.js +214 -0
- package/dist/esm/module-federation/consume-surface.mjs +3 -13
- package/dist/esm/module-federation/distributed-ssr.mjs +6 -1
- package/dist/esm/module-federation/index.mjs +1 -111
- package/dist/esm/module-federation/manifest-recovery-runtime-plugin.mjs +1 -1
- package/dist/esm/module-federation/native-browser-dev-client.mjs +276 -0
- package/dist/esm/module-federation/native-browser-dev-refresh.mjs +182 -0
- package/dist/esm/module-federation/native-browser-dev-runtime-plugin.mjs +183 -0
- package/dist/esm/module-federation/native-dev-refresh-runtime-plugin.mjs +229 -0
- package/dist/esm/module-federation/renderer-contract.mjs +103 -0
- package/dist/esm/module-federation/renderer-runtime-plugin.mjs +225 -0
- package/dist/esm/module-federation/telemetry.mjs +142 -0
- package/dist/esm-node/module-federation/consume-surface.mjs +3 -13
- package/dist/esm-node/module-federation/distributed-ssr.mjs +6 -1
- package/dist/esm-node/module-federation/index.mjs +1 -111
- package/dist/esm-node/module-federation/manifest-recovery-runtime-plugin.mjs +1 -1
- package/dist/esm-node/module-federation/native-browser-dev-client.mjs +277 -0
- package/dist/esm-node/module-federation/native-browser-dev-refresh.mjs +183 -0
- package/dist/esm-node/module-federation/native-browser-dev-runtime-plugin.mjs +184 -0
- package/dist/esm-node/module-federation/native-dev-refresh-runtime-plugin.mjs +230 -0
- package/dist/esm-node/module-federation/renderer-contract.mjs +104 -0
- package/dist/esm-node/module-federation/renderer-runtime-plugin.mjs +226 -0
- package/dist/esm-node/module-federation/telemetry.mjs +143 -0
- package/dist/types/module-federation/consume-surface.d.ts +5 -5
- package/dist/types/module-federation/distributed-ssr.d.ts +18 -1
- package/dist/types/module-federation/index.d.ts +1 -67
- package/dist/types/module-federation/native-browser-dev-client.d.ts +41 -0
- package/dist/types/module-federation/native-browser-dev-refresh.d.ts +86 -0
- package/dist/types/module-federation/native-browser-dev-runtime-plugin.d.ts +40 -0
- package/dist/types/module-federation/native-dev-refresh-runtime-plugin.d.ts +90 -0
- package/dist/types/module-federation/renderer-contract.d.ts +20 -0
- package/dist/types/module-federation/renderer-runtime-plugin.d.ts +98 -0
- package/dist/types/module-federation/telemetry.d.ts +71 -0
- package/package.json +102 -19
- package/src/module-federation/consume-surface.ts +18 -33
- package/src/module-federation/distributed-ssr.tsx +27 -2
- package/src/module-federation/index.ts +1 -267
- package/src/module-federation/manifest-recovery-runtime-plugin.ts +5 -1
- package/src/module-federation/native-browser-dev-client.ts +424 -0
- package/src/module-federation/native-browser-dev-refresh.ts +373 -0
- package/src/module-federation/native-browser-dev-runtime-plugin.ts +276 -0
- package/src/module-federation/native-dev-refresh-runtime-plugin.ts +445 -0
- package/src/module-federation/renderer-contract.ts +176 -0
- package/src/module-federation/renderer-runtime-plugin.ts +401 -0
- package/src/module-federation/telemetry.ts +297 -0
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
const MODULE_FEDERATION_FALLBACK_SIGNAL_EVENT = 'modernjs:mf-runtime-fallback';
|
|
2
|
+
const MODULE_FEDERATION_RECOVERY_SIGNAL_EVENT = 'modernjs:mf-runtime-recovery';
|
|
3
|
+
const DEFAULT_RUNTIME_FALLBACK_SIGNAL_ENDPOINT = '/_modern/contract-gates/runtime-fallback';
|
|
4
|
+
const DEFAULT_RUNTIME_FALLBACK_SIGNAL_AUTH_HEADER = 'x-modernjs-runtime-signal-token';
|
|
5
|
+
class ModuleFederationRemoteLoadTimeoutError extends Error {
|
|
6
|
+
constructor(remote, timeoutMs){
|
|
7
|
+
super(`Loading remote "${remote}" timed out after ${timeoutMs}ms`);
|
|
8
|
+
this.name = 'ModuleFederationRemoteLoadTimeoutError';
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
class ModuleFederationRemoteLoadError extends Error {
|
|
12
|
+
constructor(remote, attempts, causeError){
|
|
13
|
+
super(`Unable to load remote "${remote}" after ${attempts} attempt${attempts > 1 ? 's' : ''}: ${causeError.message}`);
|
|
14
|
+
this.name = 'ModuleFederationRemoteLoadError';
|
|
15
|
+
this.remote = remote;
|
|
16
|
+
this.attempts = attempts;
|
|
17
|
+
this.causeError = causeError;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
class ModuleFederationRemoteComponentContractError extends Error {
|
|
21
|
+
constructor(remote, exportName){
|
|
22
|
+
super(`Remote "${remote}" export "${exportName}" is not a valid React component`);
|
|
23
|
+
this.name = 'ModuleFederationRemoteComponentContractError';
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function toError(error) {
|
|
27
|
+
if (error instanceof Error) return error;
|
|
28
|
+
return new Error('string' == typeof error ? error : 'Unknown remote load error');
|
|
29
|
+
}
|
|
30
|
+
function classifyModuleFederationFallback(error) {
|
|
31
|
+
const normalizedError = toError(error);
|
|
32
|
+
if (normalizedError instanceof ModuleFederationRemoteLoadError) return classifyModuleFederationFallback(normalizedError.causeError);
|
|
33
|
+
if (normalizedError instanceof ModuleFederationRemoteLoadTimeoutError) return 'timeout';
|
|
34
|
+
if (normalizedError instanceof ModuleFederationRemoteComponentContractError) return 'contract';
|
|
35
|
+
const message = normalizedError.message;
|
|
36
|
+
if (/version|requiredVersion|singleton|share scope|shared module/i.test(message)) return 'version-skew';
|
|
37
|
+
if (/network|fetch|script|timeout|chunk|loading/i.test(message)) return 'network';
|
|
38
|
+
return 'remote-unavailable';
|
|
39
|
+
}
|
|
40
|
+
function createModuleFederationFallbackTelemetry(input) {
|
|
41
|
+
const error = void 0 !== input.error ? toError(input.error) : void 0;
|
|
42
|
+
const status = input.status ?? 'degraded';
|
|
43
|
+
const eventName = input.eventName ?? ('recovered' === status ? MODULE_FEDERATION_RECOVERY_SIGNAL_EVENT : MODULE_FEDERATION_FALLBACK_SIGNAL_EVENT);
|
|
44
|
+
const metadata = {
|
|
45
|
+
...input.metadata ?? {},
|
|
46
|
+
classification: input.classification,
|
|
47
|
+
remote: input.remote,
|
|
48
|
+
status
|
|
49
|
+
};
|
|
50
|
+
if (void 0 !== input.exportName) metadata.exportName = input.exportName;
|
|
51
|
+
if (void 0 !== input.runtimeDigest) metadata.runtimeDigest = input.runtimeDigest;
|
|
52
|
+
if (void 0 !== error) {
|
|
53
|
+
metadata.errorName = error.name;
|
|
54
|
+
metadata.errorMessage = error.message;
|
|
55
|
+
}
|
|
56
|
+
const payload = {
|
|
57
|
+
appName: input.appName,
|
|
58
|
+
eventName,
|
|
59
|
+
metadata,
|
|
60
|
+
phase: input.phase,
|
|
61
|
+
reason: input.classification,
|
|
62
|
+
schemaVersion: 1
|
|
63
|
+
};
|
|
64
|
+
if (void 0 !== input.entry) payload.entry = input.entry;
|
|
65
|
+
if (void 0 !== input.runtimeDigest) payload.runtimeDigest = input.runtimeDigest;
|
|
66
|
+
return payload;
|
|
67
|
+
}
|
|
68
|
+
function toModuleFederationFallbackAttributes(payload) {
|
|
69
|
+
return {
|
|
70
|
+
'data-mf-fallback-app': payload.appName,
|
|
71
|
+
'data-mf-fallback-classification': payload.reason,
|
|
72
|
+
'data-mf-fallback-phase': payload.phase,
|
|
73
|
+
'data-mf-fallback-remote': String(payload.metadata.remote),
|
|
74
|
+
'data-mf-fallback-status': String(payload.metadata.status),
|
|
75
|
+
'data-mf-telemetry-event': payload.eventName
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
function emitModuleFederationFallbackTelemetry(input, options = {}) {
|
|
79
|
+
return emitModuleFederationFallbackPayload(createModuleFederationFallbackTelemetry(input), options);
|
|
80
|
+
}
|
|
81
|
+
function emitModuleFederationFallbackPayload(payload, options = {}) {
|
|
82
|
+
const delivery = deliver(payload, options);
|
|
83
|
+
try {
|
|
84
|
+
options.observe?.(payload, delivery);
|
|
85
|
+
} catch {}
|
|
86
|
+
return delivery;
|
|
87
|
+
}
|
|
88
|
+
async function deliver(payload, options) {
|
|
89
|
+
let dispatched = false;
|
|
90
|
+
let timer;
|
|
91
|
+
const controller = new AbortController();
|
|
92
|
+
try {
|
|
93
|
+
if ("u" > typeof window && 'function' == typeof window.dispatchEvent && "u" > typeof CustomEvent) {
|
|
94
|
+
window.dispatchEvent(new CustomEvent(payload.eventName, {
|
|
95
|
+
detail: payload
|
|
96
|
+
}));
|
|
97
|
+
dispatched = true;
|
|
98
|
+
}
|
|
99
|
+
const fetchImpl = options.fetchImpl ?? globalThis.fetch;
|
|
100
|
+
if (!(true === options.postSignal || options.endpoint) || 'function' != typeof fetchImpl) return {
|
|
101
|
+
dispatched,
|
|
102
|
+
posted: false
|
|
103
|
+
};
|
|
104
|
+
const headers = new Headers({
|
|
105
|
+
'content-type': 'application/json'
|
|
106
|
+
});
|
|
107
|
+
if (options.authToken) headers.set(options.authHeaderName ?? DEFAULT_RUNTIME_FALLBACK_SIGNAL_AUTH_HEADER, options.authToken);
|
|
108
|
+
const timeoutMs = Number.isSafeInteger(options.timeoutMs) && (options.timeoutMs ?? 0) > 0 ? options.timeoutMs : 1000;
|
|
109
|
+
const expired = new Promise((resolve)=>{
|
|
110
|
+
timer = setTimeout(()=>{
|
|
111
|
+
controller.abort();
|
|
112
|
+
resolve(void 0);
|
|
113
|
+
}, timeoutMs);
|
|
114
|
+
});
|
|
115
|
+
const response = await Promise.race([
|
|
116
|
+
fetchImpl(options.endpoint ?? DEFAULT_RUNTIME_FALLBACK_SIGNAL_ENDPOINT, {
|
|
117
|
+
body: JSON.stringify(payload),
|
|
118
|
+
headers,
|
|
119
|
+
method: 'POST',
|
|
120
|
+
keepalive: true,
|
|
121
|
+
signal: controller.signal
|
|
122
|
+
}),
|
|
123
|
+
expired
|
|
124
|
+
]);
|
|
125
|
+
return response ? {
|
|
126
|
+
dispatched,
|
|
127
|
+
posted: true,
|
|
128
|
+
postStatus: response.status
|
|
129
|
+
} : {
|
|
130
|
+
dispatched,
|
|
131
|
+
posted: false
|
|
132
|
+
};
|
|
133
|
+
} catch {
|
|
134
|
+
return {
|
|
135
|
+
dispatched,
|
|
136
|
+
posted: false
|
|
137
|
+
};
|
|
138
|
+
} finally{
|
|
139
|
+
if (void 0 !== timer) clearTimeout(timer);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
export { DEFAULT_RUNTIME_FALLBACK_SIGNAL_AUTH_HEADER, DEFAULT_RUNTIME_FALLBACK_SIGNAL_ENDPOINT, MODULE_FEDERATION_FALLBACK_SIGNAL_EVENT, MODULE_FEDERATION_RECOVERY_SIGNAL_EVENT, ModuleFederationRemoteComponentContractError, ModuleFederationRemoteLoadError, ModuleFederationRemoteLoadTimeoutError, classifyModuleFederationFallback, createModuleFederationFallbackTelemetry, emitModuleFederationFallbackPayload, emitModuleFederationFallbackTelemetry, toModuleFederationFallbackAttributes };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import "node:module";
|
|
2
|
-
import { classifyModuleFederationFallback, createModuleFederationFallbackTelemetry, emitModuleFederationFallbackTelemetry } from "./index.mjs";
|
|
3
2
|
import { createDiscoveryError, formatSurfaceRef, parseSurfaceRef, validateSurfaceRef } from "./surface-resolution-types.mjs";
|
|
3
|
+
import { classifyModuleFederationFallback, createModuleFederationFallbackTelemetry, emitModuleFederationFallbackPayload, emitModuleFederationFallbackTelemetry } from "./telemetry.mjs";
|
|
4
4
|
function classifyDiscoveryError(code) {
|
|
5
5
|
switch(code){
|
|
6
6
|
case 'major-not-published':
|
|
@@ -39,17 +39,7 @@ async function reportFailure(options, ref, base) {
|
|
|
39
39
|
remote,
|
|
40
40
|
status: 'degraded'
|
|
41
41
|
});
|
|
42
|
-
|
|
43
|
-
await emitModuleFederationFallbackTelemetry({
|
|
44
|
-
appName: options.appName,
|
|
45
|
-
classification: base.classification,
|
|
46
|
-
error: base.error ?? base.discoveryError,
|
|
47
|
-
metadata: telemetry.metadata,
|
|
48
|
-
phase: base.phase,
|
|
49
|
-
remote,
|
|
50
|
-
status: 'degraded'
|
|
51
|
-
}, options.telemetry ?? {});
|
|
52
|
-
} catch {}
|
|
42
|
+
emitModuleFederationFallbackPayload(telemetry, options.telemetry);
|
|
53
43
|
const failure = {
|
|
54
44
|
ref,
|
|
55
45
|
env: options.env,
|
|
@@ -69,7 +59,7 @@ async function reportFailure(options, ref, base) {
|
|
|
69
59
|
degradedValue = await options.degraded(failure);
|
|
70
60
|
} catch (handlerError) {
|
|
71
61
|
try {
|
|
72
|
-
|
|
62
|
+
emitModuleFederationFallbackTelemetry({
|
|
73
63
|
appName: options.appName,
|
|
74
64
|
classification: base.classification,
|
|
75
65
|
error: handlerError,
|
|
@@ -63,7 +63,12 @@ function DistributedSsrBoundary({ children, expose, fallback, fragmentProps, rem
|
|
|
63
63
|
function createDistributedSsrComponent({ createComponent, expose, fallback, remote }) {
|
|
64
64
|
let RemoteComponent;
|
|
65
65
|
function DeferredRemoteComponent(props) {
|
|
66
|
-
RemoteComponent ??= createComponent(
|
|
66
|
+
RemoteComponent ??= createComponent({
|
|
67
|
+
export: 'default',
|
|
68
|
+
fallback,
|
|
69
|
+
injectLink: false,
|
|
70
|
+
loading: null
|
|
71
|
+
});
|
|
67
72
|
return /*#__PURE__*/ jsx(RemoteComponent, {
|
|
68
73
|
...props
|
|
69
74
|
});
|
|
@@ -1,116 +1,6 @@
|
|
|
1
1
|
import "node:module";
|
|
2
2
|
export * from "./consume-surface.mjs";
|
|
3
|
-
export * from "./distributed-ssr.mjs";
|
|
4
3
|
export * from "./last-known-good.mjs";
|
|
5
4
|
export * from "./manifest-recovery-runtime-plugin.mjs";
|
|
6
5
|
export * from "./surface-resolution-types.mjs";
|
|
7
|
-
|
|
8
|
-
const MODULE_FEDERATION_RECOVERY_SIGNAL_EVENT = 'modernjs:mf-runtime-recovery';
|
|
9
|
-
const DEFAULT_RUNTIME_FALLBACK_SIGNAL_ENDPOINT = '/_modern/contract-gates/runtime-fallback';
|
|
10
|
-
const DEFAULT_RUNTIME_FALLBACK_SIGNAL_AUTH_HEADER = 'x-modernjs-runtime-signal-token';
|
|
11
|
-
class ModuleFederationRemoteLoadTimeoutError extends Error {
|
|
12
|
-
constructor(remote, timeoutMs){
|
|
13
|
-
super(`Loading remote "${remote}" timed out after ${timeoutMs}ms`);
|
|
14
|
-
this.name = 'ModuleFederationRemoteLoadTimeoutError';
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
class ModuleFederationRemoteLoadError extends Error {
|
|
18
|
-
constructor(remote, attempts, causeError){
|
|
19
|
-
super(`Unable to load remote "${remote}" after ${attempts} attempt${attempts > 1 ? 's' : ''}: ${causeError.message}`);
|
|
20
|
-
this.name = 'ModuleFederationRemoteLoadError';
|
|
21
|
-
this.remote = remote;
|
|
22
|
-
this.attempts = attempts;
|
|
23
|
-
this.causeError = causeError;
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
class ModuleFederationRemoteComponentContractError extends Error {
|
|
27
|
-
constructor(remote, exportName){
|
|
28
|
-
super(`Remote "${remote}" export "${exportName}" is not a valid React component`);
|
|
29
|
-
this.name = 'ModuleFederationRemoteComponentContractError';
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
function toError(error) {
|
|
33
|
-
if (error instanceof Error) return error;
|
|
34
|
-
return new Error('string' == typeof error ? error : 'Unknown remote load error');
|
|
35
|
-
}
|
|
36
|
-
function classifyModuleFederationFallback(error) {
|
|
37
|
-
const normalizedError = toError(error);
|
|
38
|
-
if (normalizedError instanceof ModuleFederationRemoteLoadError) return classifyModuleFederationFallback(normalizedError.causeError);
|
|
39
|
-
if (normalizedError instanceof ModuleFederationRemoteLoadTimeoutError) return 'timeout';
|
|
40
|
-
if (normalizedError instanceof ModuleFederationRemoteComponentContractError) return 'contract';
|
|
41
|
-
const message = normalizedError.message;
|
|
42
|
-
if (/version|requiredVersion|singleton|share scope|shared module/i.test(message)) return 'version-skew';
|
|
43
|
-
if (/network|fetch|script|timeout|chunk|loading/i.test(message)) return 'network';
|
|
44
|
-
return 'remote-unavailable';
|
|
45
|
-
}
|
|
46
|
-
function createModuleFederationFallbackTelemetry(input) {
|
|
47
|
-
const error = void 0 !== input.error ? toError(input.error) : void 0;
|
|
48
|
-
const status = input.status ?? 'degraded';
|
|
49
|
-
const eventName = input.eventName ?? ('recovered' === status ? MODULE_FEDERATION_RECOVERY_SIGNAL_EVENT : MODULE_FEDERATION_FALLBACK_SIGNAL_EVENT);
|
|
50
|
-
const metadata = {
|
|
51
|
-
...input.metadata ?? {},
|
|
52
|
-
classification: input.classification,
|
|
53
|
-
remote: input.remote,
|
|
54
|
-
status
|
|
55
|
-
};
|
|
56
|
-
if (void 0 !== input.exportName) metadata.exportName = input.exportName;
|
|
57
|
-
if (void 0 !== input.runtimeDigest) metadata.runtimeDigest = input.runtimeDigest;
|
|
58
|
-
if (void 0 !== error) {
|
|
59
|
-
metadata.errorName = error.name;
|
|
60
|
-
metadata.errorMessage = error.message;
|
|
61
|
-
}
|
|
62
|
-
const payload = {
|
|
63
|
-
appName: input.appName,
|
|
64
|
-
eventName,
|
|
65
|
-
metadata,
|
|
66
|
-
phase: input.phase,
|
|
67
|
-
reason: input.classification,
|
|
68
|
-
schemaVersion: 1
|
|
69
|
-
};
|
|
70
|
-
if (void 0 !== input.entry) payload.entry = input.entry;
|
|
71
|
-
if (void 0 !== input.runtimeDigest) payload.runtimeDigest = input.runtimeDigest;
|
|
72
|
-
return payload;
|
|
73
|
-
}
|
|
74
|
-
function toModuleFederationFallbackAttributes(payload) {
|
|
75
|
-
return {
|
|
76
|
-
'data-mf-fallback-app': payload.appName,
|
|
77
|
-
'data-mf-fallback-classification': payload.reason,
|
|
78
|
-
'data-mf-fallback-phase': payload.phase,
|
|
79
|
-
'data-mf-fallback-remote': String(payload.metadata.remote),
|
|
80
|
-
'data-mf-fallback-status': String(payload.metadata.status),
|
|
81
|
-
'data-mf-telemetry-event': payload.eventName
|
|
82
|
-
};
|
|
83
|
-
}
|
|
84
|
-
async function emitModuleFederationFallbackTelemetry(input, options = {}) {
|
|
85
|
-
const payload = createModuleFederationFallbackTelemetry(input);
|
|
86
|
-
if ("u" > typeof window && 'function' == typeof window.dispatchEvent && "u" > typeof CustomEvent) window.dispatchEvent(new CustomEvent(payload.eventName, {
|
|
87
|
-
detail: payload
|
|
88
|
-
}));
|
|
89
|
-
const shouldPost = true === options.postSignal || Boolean(options.endpoint);
|
|
90
|
-
if (!shouldPost) return {
|
|
91
|
-
dispatched: true,
|
|
92
|
-
posted: false
|
|
93
|
-
};
|
|
94
|
-
const fetchImpl = options.fetchImpl ?? globalThis.fetch;
|
|
95
|
-
if ('function' != typeof fetchImpl) return {
|
|
96
|
-
dispatched: true,
|
|
97
|
-
posted: false
|
|
98
|
-
};
|
|
99
|
-
const headers = new Headers({
|
|
100
|
-
'content-type': 'application/json'
|
|
101
|
-
});
|
|
102
|
-
if (void 0 !== options.authToken && options.authToken.length > 0) headers.set(options.authHeaderName ?? DEFAULT_RUNTIME_FALLBACK_SIGNAL_AUTH_HEADER, options.authToken);
|
|
103
|
-
const response = await fetchImpl(options.endpoint ?? DEFAULT_RUNTIME_FALLBACK_SIGNAL_ENDPOINT, {
|
|
104
|
-
body: JSON.stringify(payload),
|
|
105
|
-
headers,
|
|
106
|
-
method: 'POST',
|
|
107
|
-
keepalive: true
|
|
108
|
-
});
|
|
109
|
-
return {
|
|
110
|
-
dispatched: true,
|
|
111
|
-
posted: true,
|
|
112
|
-
postStatus: response.status
|
|
113
|
-
};
|
|
114
|
-
}
|
|
115
|
-
export { DISTRIBUTED_SSR_FRAGMENT_REQUEST_LOCALS_KEY, useDistributedSsrFragmentProps } from "./distributed-ssr.mjs";
|
|
116
|
-
export { DEFAULT_RUNTIME_FALLBACK_SIGNAL_AUTH_HEADER, DEFAULT_RUNTIME_FALLBACK_SIGNAL_ENDPOINT, MODULE_FEDERATION_FALLBACK_SIGNAL_EVENT, MODULE_FEDERATION_RECOVERY_SIGNAL_EVENT, ModuleFederationRemoteComponentContractError, ModuleFederationRemoteLoadError, ModuleFederationRemoteLoadTimeoutError, classifyModuleFederationFallback, createModuleFederationFallbackTelemetry, emitModuleFederationFallbackTelemetry, toModuleFederationFallbackAttributes };
|
|
6
|
+
export * from "./telemetry.mjs";
|
|
@@ -32,7 +32,7 @@ const getErrorStatus = (error)=>{
|
|
|
32
32
|
if ('object' == typeof error && null !== error && 'status' in error && 'number' == typeof error.status) return error.status;
|
|
33
33
|
};
|
|
34
34
|
const isRetryableError = (error)=>{
|
|
35
|
-
if (error instanceof SyntaxError) return
|
|
35
|
+
if (error instanceof SyntaxError) return true;
|
|
36
36
|
const message = getErrorMessage(error);
|
|
37
37
|
if (NON_RETRYABLE_ERROR_PATTERNS.some((pattern)=>pattern.test(message))) return false;
|
|
38
38
|
const status = getErrorStatus(error);
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
import "node:module";
|
|
2
|
+
import { isNativeBrowserDevCommitError } from "./native-browser-dev-refresh.mjs";
|
|
3
|
+
import { hasNativeBrowserDevPublisher, readNativeBrowserDevPublication, replaceNativeBrowserDevRemote } from "./native-browser-dev-runtime-plugin.mjs";
|
|
4
|
+
const CLIENTS = Symbol.for('ultramodern.federation.browser-dev-clients.v1');
|
|
5
|
+
const clients = ()=>{
|
|
6
|
+
const root = globalThis;
|
|
7
|
+
if (!root[CLIENTS]) Object.defineProperty(root, CLIENTS, {
|
|
8
|
+
value: new Map()
|
|
9
|
+
});
|
|
10
|
+
return root[CLIENTS];
|
|
11
|
+
};
|
|
12
|
+
function commitClient(remoteName, graph) {
|
|
13
|
+
const state = clients().get(remoteName);
|
|
14
|
+
const next = state?.candidates.get(graph.runtimeHash)?.deref();
|
|
15
|
+
if (!state || !next) throw new Error('Accepted native graph has no matching fresh HMR runtime.');
|
|
16
|
+
if (state.active === next) return;
|
|
17
|
+
state.active?.stop();
|
|
18
|
+
state.active = next;
|
|
19
|
+
next.start();
|
|
20
|
+
}
|
|
21
|
+
function prepareNativeBrowserDevClient(remoteName, manifest) {
|
|
22
|
+
const hash = /\/__ultramodern_mf__\/([a-f0-9]+)\//u.exec(new URL(manifest).pathname)?.[1];
|
|
23
|
+
const candidate = hash && clients().get(remoteName)?.candidates.get(hash)?.deref();
|
|
24
|
+
if (!candidate) throw new Error('Native graph has no loaded standby HMR runtime.');
|
|
25
|
+
return candidate.lease();
|
|
26
|
+
}
|
|
27
|
+
function releaseNativeBrowserDevClient(remoteName, manifest) {
|
|
28
|
+
const hash = /\/__ultramodern_mf__\/([a-f0-9]+)\//u.exec(new URL(manifest).pathname)?.[1];
|
|
29
|
+
if (!hash) return;
|
|
30
|
+
const state = clients().get(remoteName);
|
|
31
|
+
const candidate = state?.candidates.get(hash)?.deref();
|
|
32
|
+
if (!state || !candidate) return;
|
|
33
|
+
candidate.releaseStandby();
|
|
34
|
+
}
|
|
35
|
+
function startNativeBrowserDevClient(options) {
|
|
36
|
+
const state = clients().get(options.remoteName) ?? {
|
|
37
|
+
candidates: new Map()
|
|
38
|
+
};
|
|
39
|
+
clients().set(options.remoteName, state);
|
|
40
|
+
const initialHash = options.hash();
|
|
41
|
+
let socket;
|
|
42
|
+
let ping;
|
|
43
|
+
let reconnect;
|
|
44
|
+
let reconnectCount = 0;
|
|
45
|
+
let lastHash;
|
|
46
|
+
let running = false;
|
|
47
|
+
let applying;
|
|
48
|
+
let lifetime;
|
|
49
|
+
let removeCustom;
|
|
50
|
+
const custom = new Map();
|
|
51
|
+
const queued = [];
|
|
52
|
+
const leases = new Set();
|
|
53
|
+
const base = options.serverBase.endsWith('/') ? options.serverBase : `${options.serverBase}/`;
|
|
54
|
+
const report = (error)=>{
|
|
55
|
+
console.error('[ultramodern] Native federation development update failed:', error);
|
|
56
|
+
if (false !== options.config.overlay && ('object' != typeof options.config.overlay || false !== options.config.overlay.errors)) options.overlay()?.create('Native federation update failed', String(error));
|
|
57
|
+
};
|
|
58
|
+
const send = (message)=>{
|
|
59
|
+
if (socket?.readyState === WebSocket.OPEN) socket.send(JSON.stringify(message));
|
|
60
|
+
else queued.push(message);
|
|
61
|
+
};
|
|
62
|
+
const errorMessage = (error)=>error instanceof Error ? error.message : String(error);
|
|
63
|
+
const onError = (event)=>send({
|
|
64
|
+
type: 'client-error',
|
|
65
|
+
id: crypto.randomUUID(),
|
|
66
|
+
message: event.message,
|
|
67
|
+
name: event.error?.name,
|
|
68
|
+
stack: event.error?.stack
|
|
69
|
+
});
|
|
70
|
+
const onRejected = (event)=>send({
|
|
71
|
+
type: 'client-error',
|
|
72
|
+
id: crypto.randomUUID(),
|
|
73
|
+
message: `Uncaught (in promise) ${errorMessage(event.reason)}`,
|
|
74
|
+
name: event.reason?.name,
|
|
75
|
+
stack: event.reason?.stack
|
|
76
|
+
});
|
|
77
|
+
const fullReload = (data)=>{
|
|
78
|
+
if (!options.liveReload) return;
|
|
79
|
+
if (data?.path?.endsWith('.html')) {
|
|
80
|
+
const pathname = decodeURI(location.pathname);
|
|
81
|
+
const target = base + data.path.slice(1);
|
|
82
|
+
if (pathname !== target && pathname !== target.slice(0, -5) && !(pathname.endsWith('/') && `${pathname}index.html` === target)) return;
|
|
83
|
+
}
|
|
84
|
+
location.reload();
|
|
85
|
+
};
|
|
86
|
+
const apply = ()=>{
|
|
87
|
+
if (!running || applying || !lastHash || lastHash === options.hash()) return;
|
|
88
|
+
const signal = lifetime.signal;
|
|
89
|
+
const targetHash = lastHash;
|
|
90
|
+
applying = (async ()=>{
|
|
91
|
+
if (hasNativeBrowserDevPublisher(options.remoteName)) {
|
|
92
|
+
const graph = await readNativeBrowserDevPublication(options.remoteName, {
|
|
93
|
+
signal: AbortSignal.any([
|
|
94
|
+
signal,
|
|
95
|
+
AbortSignal.timeout(3000)
|
|
96
|
+
]),
|
|
97
|
+
revisionHeader: options.revisionHeader
|
|
98
|
+
});
|
|
99
|
+
if (signal.aborted) throw signal.reason;
|
|
100
|
+
if (graph.buildId !== options.buildId) {
|
|
101
|
+
try {
|
|
102
|
+
await replaceNativeBrowserDevRemote(graph);
|
|
103
|
+
} catch (error) {
|
|
104
|
+
if (running && !signal.aborted && hasNativeBrowserDevPublisher(options.remoteName) && isNativeBrowserDevCommitError(error, graph.revision)) {
|
|
105
|
+
state.failure = {
|
|
106
|
+
runtimeHash: graph.runtimeHash,
|
|
107
|
+
error
|
|
108
|
+
};
|
|
109
|
+
report(error);
|
|
110
|
+
commitClient(options.remoteName, graph);
|
|
111
|
+
}
|
|
112
|
+
throw error;
|
|
113
|
+
}
|
|
114
|
+
if (!running || signal.aborted || !hasNativeBrowserDevPublisher(options.remoteName)) return;
|
|
115
|
+
commitClient(options.remoteName, graph);
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
if (!options.hot) return fullReload();
|
|
120
|
+
if ('idle' !== options.hot.status()) return;
|
|
121
|
+
try {
|
|
122
|
+
const updated = await options.hot.check(true);
|
|
123
|
+
if (!updated) fullReload();
|
|
124
|
+
} catch (error) {
|
|
125
|
+
console.error('[rsbuild] HMR update failed, performing full reload:', error);
|
|
126
|
+
fullReload();
|
|
127
|
+
}
|
|
128
|
+
})().catch((error)=>{
|
|
129
|
+
if (!signal.aborted) report(error);
|
|
130
|
+
}).finally(()=>{
|
|
131
|
+
applying = void 0;
|
|
132
|
+
if (running && !signal.aborted && lastHash !== targetHash) apply();
|
|
133
|
+
});
|
|
134
|
+
};
|
|
135
|
+
const clearOverlayForBuild = ()=>{
|
|
136
|
+
if (state.failure) {
|
|
137
|
+
if (!lastHash || lastHash === state.failure.runtimeHash) return;
|
|
138
|
+
state.failure = void 0;
|
|
139
|
+
}
|
|
140
|
+
options.overlay()?.clear();
|
|
141
|
+
};
|
|
142
|
+
const message = (event)=>{
|
|
143
|
+
let value;
|
|
144
|
+
try {
|
|
145
|
+
value = JSON.parse(event.data);
|
|
146
|
+
} catch (error) {
|
|
147
|
+
report(error);
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
switch(value.type){
|
|
151
|
+
case 'hash':
|
|
152
|
+
lastHash = value.data;
|
|
153
|
+
break;
|
|
154
|
+
case 'ok':
|
|
155
|
+
clearOverlayForBuild();
|
|
156
|
+
apply();
|
|
157
|
+
break;
|
|
158
|
+
case 'warnings':
|
|
159
|
+
for (const warning of value.data?.text ?? [])console.warn(warning);
|
|
160
|
+
clearOverlayForBuild();
|
|
161
|
+
apply();
|
|
162
|
+
break;
|
|
163
|
+
case 'errors':
|
|
164
|
+
for (const error of value.data?.text ?? [])console.error(error);
|
|
165
|
+
if (value.data?.html) options.overlay()?.create('Build failed', value.data.html);
|
|
166
|
+
break;
|
|
167
|
+
case 'full-reload':
|
|
168
|
+
fullReload(value.data);
|
|
169
|
+
break;
|
|
170
|
+
case 'static-changed':
|
|
171
|
+
fullReload();
|
|
172
|
+
break;
|
|
173
|
+
case 'custom':
|
|
174
|
+
for (const listener of custom.get(value.data?.event) ?? [])listener(value.data.data);
|
|
175
|
+
break;
|
|
176
|
+
case 'resolved-client-error':
|
|
177
|
+
options.overlay()?.create('Runtime errors', String(value.data?.message ?? ''));
|
|
178
|
+
break;
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
const socketUrl = (fallback)=>{
|
|
182
|
+
const url = new URL('http://localhost');
|
|
183
|
+
url.hostname = (fallback ? options.serverHost : options.config.host) || location.hostname;
|
|
184
|
+
url.port = String((fallback ? options.serverPort : options.config.port) || location.port);
|
|
185
|
+
url.protocol = options.config.protocol || ('https:' === location.protocol ? 'wss:' : 'ws:');
|
|
186
|
+
url.pathname = options.config.path;
|
|
187
|
+
url.searchParams.set('token', options.token);
|
|
188
|
+
return options.resolveWebSocketUrl?.(url.href) ?? url.href;
|
|
189
|
+
};
|
|
190
|
+
const detachSocket = ()=>{
|
|
191
|
+
clearInterval(ping);
|
|
192
|
+
ping = void 0;
|
|
193
|
+
const previous = socket;
|
|
194
|
+
socket = void 0;
|
|
195
|
+
if (!previous) return;
|
|
196
|
+
previous.onopen = previous.onclose = previous.onmessage = previous.onerror = null;
|
|
197
|
+
previous.close();
|
|
198
|
+
};
|
|
199
|
+
const connect = (fallback = false)=>{
|
|
200
|
+
if (!running) return;
|
|
201
|
+
socket = new WebSocket(socketUrl(fallback));
|
|
202
|
+
socket.onopen = ()=>{
|
|
203
|
+
reconnectCount = 0;
|
|
204
|
+
ping = setInterval(()=>send({
|
|
205
|
+
type: 'ping'
|
|
206
|
+
}), 30000);
|
|
207
|
+
for (const item of queued.splice(0))send(item);
|
|
208
|
+
};
|
|
209
|
+
socket.onmessage = message;
|
|
210
|
+
socket.onclose = ()=>{
|
|
211
|
+
detachSocket();
|
|
212
|
+
if (!running || reconnectCount >= options.config.reconnect) return;
|
|
213
|
+
reconnectCount++;
|
|
214
|
+
reconnect = setTimeout(()=>connect(), 1000 * 1.5 ** reconnectCount);
|
|
215
|
+
};
|
|
216
|
+
socket.onerror = ()=>{
|
|
217
|
+
if (options.resolveWebSocketUrl || fallback || socketUrl(false) === socketUrl(true)) return;
|
|
218
|
+
detachSocket();
|
|
219
|
+
connect(true);
|
|
220
|
+
};
|
|
221
|
+
};
|
|
222
|
+
const client = {
|
|
223
|
+
arm () {
|
|
224
|
+
removeCustom ??= options.installCustomListeners(custom);
|
|
225
|
+
},
|
|
226
|
+
lease () {
|
|
227
|
+
const lease = {};
|
|
228
|
+
leases.add(lease);
|
|
229
|
+
client.arm();
|
|
230
|
+
return ()=>{
|
|
231
|
+
if (!leases.delete(lease)) return;
|
|
232
|
+
client.releaseStandby();
|
|
233
|
+
};
|
|
234
|
+
},
|
|
235
|
+
releaseStandby () {
|
|
236
|
+
if (state.active !== client && !leases.size) client.stop();
|
|
237
|
+
},
|
|
238
|
+
start () {
|
|
239
|
+
if (running) return;
|
|
240
|
+
running = true;
|
|
241
|
+
lifetime = new AbortController();
|
|
242
|
+
client.arm();
|
|
243
|
+
if (options.browserLogs) {
|
|
244
|
+
window.addEventListener('error', onError);
|
|
245
|
+
window.addEventListener('unhandledrejection', onRejected);
|
|
246
|
+
}
|
|
247
|
+
connect();
|
|
248
|
+
},
|
|
249
|
+
stop () {
|
|
250
|
+
if (!running && !removeCustom) return;
|
|
251
|
+
running = false;
|
|
252
|
+
lifetime?.abort(new Error('Native browser HMR runtime retired.'));
|
|
253
|
+
clearTimeout(reconnect);
|
|
254
|
+
reconnect = void 0;
|
|
255
|
+
detachSocket();
|
|
256
|
+
window.removeEventListener('error', onError);
|
|
257
|
+
window.removeEventListener('unhandledrejection', onRejected);
|
|
258
|
+
removeCustom?.();
|
|
259
|
+
removeCustom = void 0;
|
|
260
|
+
custom.clear();
|
|
261
|
+
queued.length = 0;
|
|
262
|
+
}
|
|
263
|
+
};
|
|
264
|
+
client.arm();
|
|
265
|
+
state.candidates.set(initialHash, new WeakRef(client));
|
|
266
|
+
if (!state.active) {
|
|
267
|
+
state.active = client;
|
|
268
|
+
client.start();
|
|
269
|
+
}
|
|
270
|
+
return ()=>{
|
|
271
|
+
client.stop();
|
|
272
|
+
if (state.active === client) state.active = void 0;
|
|
273
|
+
if (state.candidates.get(initialHash)?.deref() === client) state.candidates.delete(initialHash);
|
|
274
|
+
if (!state.active && !state.candidates.size) clients().delete(options.remoteName);
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
export { prepareNativeBrowserDevClient, releaseNativeBrowserDevClient, startNativeBrowserDevClient };
|