@bleedingdev/modern-js-renderer-octane 3.9.3-ultramodern.7 → 3.9.3-ultramodern.9
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/client.js +56 -6
- package/dist/cjs/entry-application.js +7 -2
- package/dist/cjs/entry-client.js +3 -0
- package/dist/cjs/entry-server.js +13 -5
- package/dist/cjs/federation.js +108 -6
- package/dist/cjs/i18n/I18nProvider.js +6 -14
- package/dist/cjs/i18n/LocalizedLink.js +24 -33
- package/dist/cjs/i18n/index.js +0 -3
- package/dist/cjs/i18n-external-snapshot.js +645 -0
- package/dist/cjs/index.js +4 -4
- package/dist/cjs/manifest.js +2 -2
- package/dist/cjs/plugin/compiler.js +2 -5
- package/dist/cjs/plugin/index.js +2 -2
- package/dist/cjs/plugin/profile.js +12 -10
- package/dist/cjs/router-client.js +1 -1
- package/dist/cjs/router-external-snapshot.js +263 -0
- package/dist/cjs/router-handler.js +11 -8
- package/dist/cjs/router-server.js +3 -11
- package/dist/cjs/router.js +1 -1
- package/dist/cjs/routes.js +11 -17
- package/dist/cjs/server.js +28 -7
- package/dist/cjs/worker-federation-context.js +63 -0
- package/dist/cjs/worker-federation-contexts.js +97 -0
- package/dist/cjs/worker-federation.js +97 -0
- package/dist/esm/client.mjs +56 -6
- package/dist/esm/entry-application.mjs +7 -2
- package/dist/esm/entry-client.mjs +3 -0
- package/dist/esm/entry-server.mjs +11 -6
- package/dist/esm/federation.mjs +110 -8
- package/dist/esm/i18n/I18nProvider.mjs +7 -15
- package/dist/esm/i18n/LocalizedLink.mjs +24 -30
- package/dist/esm/i18n/index.mjs +1 -1
- package/dist/esm/i18n-external-snapshot.mjs +576 -0
- package/dist/esm/index.mjs +4 -4
- package/dist/esm/manifest.mjs +2 -2
- package/dist/esm/router-client.mjs +1 -1
- package/dist/esm/router-external-snapshot.mjs +216 -0
- package/dist/esm/router-handler.mjs +11 -8
- package/dist/esm/router-server.mjs +0 -1
- package/dist/esm/router.mjs +2 -2
- package/dist/esm/routes.mjs +11 -17
- package/dist/esm/server.mjs +24 -3
- package/dist/esm/worker-federation-context.mjs +19 -0
- package/dist/esm/worker-federation-contexts.mjs +47 -0
- package/dist/esm/worker-federation.mjs +59 -0
- package/dist/esm-node/manifest.mjs +2 -2
- package/dist/esm-node/plugin/compiler.mjs +2 -5
- package/dist/esm-node/plugin/index.mjs +2 -2
- package/dist/esm-node/plugin/profile.mjs +12 -10
- package/dist/types/client.d.ts +5 -1
- package/dist/types/entry-client.d.ts +2 -0
- package/dist/types/entry-server.d.ts +4 -1
- package/dist/types/federation.d.ts +2 -0
- package/dist/types/i18n/LocalizedLink.d.ts +5 -32
- package/dist/types/i18n/context.d.ts +1 -1
- package/dist/types/i18n/index.d.ts +1 -1
- package/dist/types/i18n-external-snapshot.d.ts +43 -0
- package/dist/types/index.d.ts +4 -4
- package/dist/types/manifest.d.ts +2 -2
- package/dist/types/router-external-snapshot.d.ts +10 -0
- package/dist/types/router-server.d.ts +0 -2
- package/dist/types/server.d.ts +6 -0
- package/dist/types/worker-federation-context.d.ts +34 -0
- package/dist/types/worker-federation-contexts.d.ts +25 -0
- package/dist/types/worker-federation.d.ts +20 -0
- package/package.json +84 -22
- package/src/client.ts +99 -16
- package/src/entry-application.ts +7 -3
- package/src/entry-client.ts +10 -0
- package/src/entry-server.ts +20 -9
- package/src/federation.ts +159 -15
- package/src/i18n/I18nProvider.ts +11 -20
- package/src/i18n/LocalizedLink.ts +46 -72
- package/src/i18n/context.ts +1 -1
- package/src/i18n/index.ts +1 -5
- package/src/i18n-external-snapshot.ts +803 -0
- package/src/index.ts +4 -4
- package/src/manifest.ts +2 -2
- package/src/plugin/compiler-manifest.cjs +27 -1
- package/src/plugin/compiler.ts +6 -14
- package/src/plugin/index.ts +4 -2
- package/src/plugin/profile.ts +14 -10
- package/src/router-client.ts +1 -1
- package/src/router-external-snapshot.ts +306 -0
- package/src/router-handler.ts +11 -8
- package/src/router-server.ts +0 -2
- package/src/router.ts +2 -2
- package/src/routes.ts +28 -26
- package/src/server.ts +45 -5
- package/src/worker-federation-context.ts +51 -0
- package/src/worker-federation-contexts.ts +91 -0
- package/src/worker-federation.ts +120 -0
- package/dist/cjs/router-injection.js +0 -139
- package/dist/esm/router-injection.mjs +0 -101
- package/dist/types/router-injection.d.ts +0 -8
- package/src/router-injection.ts +0 -145
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __webpack_require__ = {};
|
|
3
|
+
(()=>{
|
|
4
|
+
__webpack_require__.d = (exports1, getters, values)=>{
|
|
5
|
+
var define = (defs, kind)=>{
|
|
6
|
+
for(var key in defs)if (__webpack_require__.o(defs, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
|
|
7
|
+
enumerable: true,
|
|
8
|
+
[kind]: defs[key]
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
define(getters, "get");
|
|
12
|
+
define(values, "value");
|
|
13
|
+
};
|
|
14
|
+
})();
|
|
15
|
+
(()=>{
|
|
16
|
+
__webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
|
|
17
|
+
})();
|
|
18
|
+
(()=>{
|
|
19
|
+
__webpack_require__.r = (exports1)=>{
|
|
20
|
+
if ("u" > typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
|
|
21
|
+
value: 'Module'
|
|
22
|
+
});
|
|
23
|
+
Object.defineProperty(exports1, '__esModule', {
|
|
24
|
+
value: true
|
|
25
|
+
});
|
|
26
|
+
};
|
|
27
|
+
})();
|
|
28
|
+
var __webpack_exports__ = {};
|
|
29
|
+
__webpack_require__.r(__webpack_exports__);
|
|
30
|
+
__webpack_require__.d(__webpack_exports__, {
|
|
31
|
+
WorkerFederationApplication: ()=>WorkerFederationApplication,
|
|
32
|
+
WorkerFederationContext: ()=>WorkerFederationContext,
|
|
33
|
+
WorkerFederationRoot: ()=>WorkerFederationRoot
|
|
34
|
+
});
|
|
35
|
+
const external_octane_namespaceObject = require("octane");
|
|
36
|
+
const external_federation_context_js_namespaceObject = require("./federation-context.js");
|
|
37
|
+
const WorkerFederationContext = (0, external_octane_namespaceObject.createContext)(null);
|
|
38
|
+
function WorkerFederationRoot(props) {
|
|
39
|
+
return (0, external_octane_namespaceObject.createElement)(WorkerFederationContext, {
|
|
40
|
+
value: props.scope,
|
|
41
|
+
children: props.children
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
function WorkerFederationApplication(props) {
|
|
45
|
+
return (0, external_octane_namespaceObject.createElement)(WorkerFederationRoot, {
|
|
46
|
+
scope: props.scope,
|
|
47
|
+
children: props.federation ? (0, external_octane_namespaceObject.createElement)(external_federation_context_js_namespaceObject.FederationApplication, {
|
|
48
|
+
scope: props.federation,
|
|
49
|
+
application: props.application
|
|
50
|
+
}) : (0, external_octane_namespaceObject.createElement)(props.application.default, props.application.props)
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
exports.WorkerFederationApplication = __webpack_exports__.WorkerFederationApplication;
|
|
54
|
+
exports.WorkerFederationContext = __webpack_exports__.WorkerFederationContext;
|
|
55
|
+
exports.WorkerFederationRoot = __webpack_exports__.WorkerFederationRoot;
|
|
56
|
+
for(var __rspack_i in __webpack_exports__)if (-1 === [
|
|
57
|
+
"WorkerFederationApplication",
|
|
58
|
+
"WorkerFederationContext",
|
|
59
|
+
"WorkerFederationRoot"
|
|
60
|
+
].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
|
|
61
|
+
Object.defineProperty(exports, '__esModule', {
|
|
62
|
+
value: true
|
|
63
|
+
});
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __webpack_require__ = {};
|
|
3
|
+
(()=>{
|
|
4
|
+
__webpack_require__.d = (exports1, getters, values)=>{
|
|
5
|
+
var define = (defs, kind)=>{
|
|
6
|
+
for(var key in defs)if (__webpack_require__.o(defs, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
|
|
7
|
+
enumerable: true,
|
|
8
|
+
[kind]: defs[key]
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
define(getters, "get");
|
|
12
|
+
define(values, "value");
|
|
13
|
+
};
|
|
14
|
+
})();
|
|
15
|
+
(()=>{
|
|
16
|
+
__webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
|
|
17
|
+
})();
|
|
18
|
+
(()=>{
|
|
19
|
+
__webpack_require__.r = (exports1)=>{
|
|
20
|
+
if ("u" > typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
|
|
21
|
+
value: 'Module'
|
|
22
|
+
});
|
|
23
|
+
Object.defineProperty(exports1, '__esModule', {
|
|
24
|
+
value: true
|
|
25
|
+
});
|
|
26
|
+
};
|
|
27
|
+
})();
|
|
28
|
+
var __webpack_exports__ = {};
|
|
29
|
+
__webpack_require__.r(__webpack_exports__);
|
|
30
|
+
__webpack_require__.d(__webpack_exports__, {
|
|
31
|
+
OCTANE_WORKER_CONTEXT_KEYS: ()=>OCTANE_WORKER_CONTEXT_KEYS,
|
|
32
|
+
getOctaneWorkerRouterRewrite: ()=>getOctaneWorkerRouterRewrite,
|
|
33
|
+
registerOctaneWorkerContext: ()=>registerOctaneWorkerContext,
|
|
34
|
+
registerOctaneWorkerRouterRewrite: ()=>registerOctaneWorkerRouterRewrite,
|
|
35
|
+
useOctaneWorkerContextKeys: ()=>useOctaneWorkerContextKeys
|
|
36
|
+
});
|
|
37
|
+
const external_octane_namespaceObject = require("octane");
|
|
38
|
+
const OCTANE_WORKER_CONTEXT_KEYS = Object.freeze({
|
|
39
|
+
router: 'ultramodern.octane.router-context.v1',
|
|
40
|
+
match: 'ultramodern.octane.match-context.v1',
|
|
41
|
+
i18n: 'ultramodern.octane.i18n-context.v1'
|
|
42
|
+
});
|
|
43
|
+
const builtinKeys = Object.values(OCTANE_WORKER_CONTEXT_KEYS);
|
|
44
|
+
const contexts = new Map();
|
|
45
|
+
function registerOctaneWorkerContext(context, codec) {
|
|
46
|
+
const { key, isProvided } = codec;
|
|
47
|
+
if (!builtinKeys.includes(key) || 'function' != typeof isProvided) throw new TypeError('Invalid Octane Worker builtin context codec');
|
|
48
|
+
const unregister = (0, external_octane_namespaceObject.registerExternalSnapshotContext)(context, codec);
|
|
49
|
+
const hasProvider = ()=>isProvided((0, external_octane_namespaceObject.useContext)(context));
|
|
50
|
+
contexts.set(key, hasProvider);
|
|
51
|
+
return ()=>{
|
|
52
|
+
if (contexts.get(key) !== hasProvider) return;
|
|
53
|
+
unregister();
|
|
54
|
+
contexts.delete(key);
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
function useOctaneWorkerContextKeys(explicit = []) {
|
|
58
|
+
const selected = [];
|
|
59
|
+
for (const key of builtinKeys){
|
|
60
|
+
const hasProvider = contexts.get(key);
|
|
61
|
+
if (hasProvider?.() && !explicit.includes(key)) selected.push(key);
|
|
62
|
+
}
|
|
63
|
+
return [
|
|
64
|
+
...selected,
|
|
65
|
+
...explicit
|
|
66
|
+
];
|
|
67
|
+
}
|
|
68
|
+
let routerRewrite;
|
|
69
|
+
function registerOctaneWorkerRouterRewrite(codec) {
|
|
70
|
+
if (routerRewrite || 'function' != typeof codec.capture || 'function' != typeof codec.restore) throw new TypeError('Invalid or duplicate Octane Worker router rewrite codec');
|
|
71
|
+
const captured = Object.freeze({
|
|
72
|
+
capture: codec.capture,
|
|
73
|
+
restore: codec.restore
|
|
74
|
+
});
|
|
75
|
+
routerRewrite = captured;
|
|
76
|
+
return ()=>{
|
|
77
|
+
if (routerRewrite === captured) routerRewrite = void 0;
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
function getOctaneWorkerRouterRewrite() {
|
|
81
|
+
return routerRewrite;
|
|
82
|
+
}
|
|
83
|
+
exports.OCTANE_WORKER_CONTEXT_KEYS = __webpack_exports__.OCTANE_WORKER_CONTEXT_KEYS;
|
|
84
|
+
exports.getOctaneWorkerRouterRewrite = __webpack_exports__.getOctaneWorkerRouterRewrite;
|
|
85
|
+
exports.registerOctaneWorkerContext = __webpack_exports__.registerOctaneWorkerContext;
|
|
86
|
+
exports.registerOctaneWorkerRouterRewrite = __webpack_exports__.registerOctaneWorkerRouterRewrite;
|
|
87
|
+
exports.useOctaneWorkerContextKeys = __webpack_exports__.useOctaneWorkerContextKeys;
|
|
88
|
+
for(var __rspack_i in __webpack_exports__)if (-1 === [
|
|
89
|
+
"OCTANE_WORKER_CONTEXT_KEYS",
|
|
90
|
+
"getOctaneWorkerRouterRewrite",
|
|
91
|
+
"registerOctaneWorkerContext",
|
|
92
|
+
"registerOctaneWorkerRouterRewrite",
|
|
93
|
+
"useOctaneWorkerContextKeys"
|
|
94
|
+
].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
|
|
95
|
+
Object.defineProperty(exports, '__esModule', {
|
|
96
|
+
value: true
|
|
97
|
+
});
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __webpack_require__ = {};
|
|
3
|
+
(()=>{
|
|
4
|
+
__webpack_require__.d = (exports1, getters, values)=>{
|
|
5
|
+
var define = (defs, kind)=>{
|
|
6
|
+
for(var key in defs)if (__webpack_require__.o(defs, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
|
|
7
|
+
enumerable: true,
|
|
8
|
+
[kind]: defs[key]
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
define(getters, "get");
|
|
12
|
+
define(values, "value");
|
|
13
|
+
};
|
|
14
|
+
})();
|
|
15
|
+
(()=>{
|
|
16
|
+
__webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
|
|
17
|
+
})();
|
|
18
|
+
(()=>{
|
|
19
|
+
__webpack_require__.r = (exports1)=>{
|
|
20
|
+
if ("u" > typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
|
|
21
|
+
value: 'Module'
|
|
22
|
+
});
|
|
23
|
+
Object.defineProperty(exports1, '__esModule', {
|
|
24
|
+
value: true
|
|
25
|
+
});
|
|
26
|
+
};
|
|
27
|
+
})();
|
|
28
|
+
var __webpack_exports__ = {};
|
|
29
|
+
__webpack_require__.r(__webpack_exports__);
|
|
30
|
+
__webpack_require__.d(__webpack_exports__, {
|
|
31
|
+
createNativeWorkerFederationRuntime: ()=>createNativeWorkerFederationRuntime
|
|
32
|
+
});
|
|
33
|
+
const worker_federation_namespaceObject = require("@modern-js/renderer-core/worker-federation");
|
|
34
|
+
const server_namespaceObject = require("octane/server");
|
|
35
|
+
const external_federation_context_js_namespaceObject = require("./federation-context.js");
|
|
36
|
+
const external_worker_federation_context_js_namespaceObject = require("./worker-federation-context.js");
|
|
37
|
+
function isNativeWorkerComponent(value) {
|
|
38
|
+
return 'function' == typeof value;
|
|
39
|
+
}
|
|
40
|
+
function createNativeWorkerFederationRuntime(options, compiler) {
|
|
41
|
+
const { resources } = options;
|
|
42
|
+
const bindings = options.bindings;
|
|
43
|
+
const transport = resources.workerFederationServices?.length ? (0, worker_federation_namespaceObject.createWorkerFederationTransport)({
|
|
44
|
+
admitRendererContract: compiler.admitRendererContract,
|
|
45
|
+
services: resources.workerFederationServices.map((service)=>{
|
|
46
|
+
const binding = bindings && Reflect.get(bindings, service.binding);
|
|
47
|
+
if (!binding || 'object' != typeof binding && 'function' != typeof binding || !('fetch' in binding) || 'function' != typeof binding.fetch) throw new Error(`Native Worker federation requires service binding ${service.binding}.`);
|
|
48
|
+
return {
|
|
49
|
+
...service,
|
|
50
|
+
fetch: binding.fetch.bind(binding)
|
|
51
|
+
};
|
|
52
|
+
})
|
|
53
|
+
}) : void 0;
|
|
54
|
+
let handle;
|
|
55
|
+
if (resources.workerFederation) {
|
|
56
|
+
if (!compiler.registry || !resources.workerFederationManifestUrl) throw new Error('Native Worker federation requires its compiled expose registry and pinned manifest URL.');
|
|
57
|
+
handle = compiler.registry.initialize({
|
|
58
|
+
publication: resources.workerFederation.worker,
|
|
59
|
+
manifestUrl: resources.workerFederationManifestUrl,
|
|
60
|
+
admitRendererContract: compiler.admitRendererContract,
|
|
61
|
+
admitNativeRequest (payload, { admitted, signal }) {
|
|
62
|
+
const initializeContexts = transport ? (provide, request)=>{
|
|
63
|
+
provide(external_federation_context_js_namespaceObject.FederationContext, (0, external_federation_context_js_namespaceObject.createFederationScope)(void 0, true, request.nonce));
|
|
64
|
+
provide(external_worker_federation_context_js_namespaceObject.WorkerFederationContext, {
|
|
65
|
+
transport,
|
|
66
|
+
locale: admitted.locale,
|
|
67
|
+
signal
|
|
68
|
+
});
|
|
69
|
+
} : void 0;
|
|
70
|
+
return (0, server_namespaceObject.prepareExternalSnapshotRequest)(payload, {
|
|
71
|
+
authority: admitted.coordinate.native,
|
|
72
|
+
signal,
|
|
73
|
+
...initializeContexts ? {
|
|
74
|
+
initializeContexts
|
|
75
|
+
} : {}
|
|
76
|
+
});
|
|
77
|
+
},
|
|
78
|
+
releaseNativeRequest: server_namespaceObject.releasePreparedExternalSnapshotRequest,
|
|
79
|
+
async renderNativeSnapshot (module, request, { admitted }) {
|
|
80
|
+
const component = module && 'object' == typeof module && 'default' in module ? module.default : void 0;
|
|
81
|
+
if (!isNativeWorkerComponent(component)) throw new TypeError(`Native Worker expose ${admitted.expose} requires a default Octane component.`);
|
|
82
|
+
return (0, server_namespaceObject.serializeExternalSnapshot)(await (0, server_namespaceObject.renderExternalSnapshot)(component, request));
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
return {
|
|
87
|
+
transport,
|
|
88
|
+
handleRequest: handle ?? (()=>Promise.resolve(void 0))
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
exports.createNativeWorkerFederationRuntime = __webpack_exports__.createNativeWorkerFederationRuntime;
|
|
92
|
+
for(var __rspack_i in __webpack_exports__)if (-1 === [
|
|
93
|
+
"createNativeWorkerFederationRuntime"
|
|
94
|
+
].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
|
|
95
|
+
Object.defineProperty(exports, '__esModule', {
|
|
96
|
+
value: true
|
|
97
|
+
});
|
package/dist/esm/client.mjs
CHANGED
|
@@ -3,6 +3,7 @@ import { createRoot, hydrateRoot } from "octane";
|
|
|
3
3
|
import { bootstrapStreamedSignalHydration } from "octane/hydration/streamed-signals";
|
|
4
4
|
import { assertNativeHydrationBuildId, assertOctaneIdentity, readOctaneDocumentBootstrap } from "./bootstrap.mjs";
|
|
5
5
|
import { FederationApplication, createFederationScope } from "./federation-context.mjs";
|
|
6
|
+
import { WorkerFederationApplication } from "./worker-federation-context.mjs";
|
|
6
7
|
const roots = new WeakMap();
|
|
7
8
|
const documents = new WeakMap();
|
|
8
9
|
const moduleResources = new WeakMap();
|
|
@@ -186,7 +187,11 @@ function createHandle(input) {
|
|
|
186
187
|
const nextRelease = claimModuleResources(next);
|
|
187
188
|
releaseModule = nextRelease;
|
|
188
189
|
try {
|
|
189
|
-
input.
|
|
190
|
+
input.workerFederation ? input.root.render(WorkerFederationApplication, {
|
|
191
|
+
scope: input.workerFederation,
|
|
192
|
+
federation: input.federation,
|
|
193
|
+
application: next
|
|
194
|
+
}) : input.federation ? input.root.render(FederationApplication, {
|
|
190
195
|
scope: input.federation,
|
|
191
196
|
application: next
|
|
192
197
|
}) : input.root.render(next.default, next.props);
|
|
@@ -204,6 +209,7 @@ function createHandle(input) {
|
|
|
204
209
|
disposed = true;
|
|
205
210
|
input.signal?.removeEventListener('abort', onAbort);
|
|
206
211
|
cleanupResources([
|
|
212
|
+
()=>input.retireWorkerFederation?.(),
|
|
207
213
|
()=>input.root.unmount(),
|
|
208
214
|
()=>releaseModule(),
|
|
209
215
|
()=>input.bridge?.dispose(),
|
|
@@ -227,6 +233,12 @@ function createHandle(input) {
|
|
|
227
233
|
}
|
|
228
234
|
async function startApplication(input, release, hydration) {
|
|
229
235
|
const errors = observeRootErrors(input.options);
|
|
236
|
+
const workerController = input.workerFederation ? new AbortController() : void 0;
|
|
237
|
+
let workerFederation;
|
|
238
|
+
const retireWorkerFederation = ()=>cleanupResources([
|
|
239
|
+
()=>workerController?.abort(new DOMException('The Worker federation document was disposed', 'AbortError')),
|
|
240
|
+
()=>workerFederation?.transport.dispose()
|
|
241
|
+
]);
|
|
230
242
|
let bridge;
|
|
231
243
|
let root;
|
|
232
244
|
let releaseModule;
|
|
@@ -237,6 +249,7 @@ async function startApplication(input, release, hydration) {
|
|
|
237
249
|
if (cleaned) return;
|
|
238
250
|
cleaned = true;
|
|
239
251
|
cleanupResources([
|
|
252
|
+
retireWorkerFederation,
|
|
240
253
|
()=>root?.unmount(),
|
|
241
254
|
()=>releaseModule?.(),
|
|
242
255
|
()=>bridge?.dispose(),
|
|
@@ -244,7 +257,24 @@ async function startApplication(input, release, hydration) {
|
|
|
244
257
|
]);
|
|
245
258
|
};
|
|
246
259
|
try {
|
|
247
|
-
|
|
260
|
+
if (input.workerFederation && workerController) {
|
|
261
|
+
const signal = AbortSignal.any([
|
|
262
|
+
workerController.signal,
|
|
263
|
+
...input.signal ? [
|
|
264
|
+
input.signal
|
|
265
|
+
] : []
|
|
266
|
+
]);
|
|
267
|
+
const scope = 'function' == typeof input.workerFederation ? input.workerFederation(signal) : input.workerFederation;
|
|
268
|
+
workerFederation = {
|
|
269
|
+
...scope,
|
|
270
|
+
signal: scope.signal ? AbortSignal.any([
|
|
271
|
+
signal,
|
|
272
|
+
scope.signal
|
|
273
|
+
]) : signal
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
if (workerFederation && !hydration && !input.documentId) throw new Error('Worker federation requires the trusted document identity before application loading');
|
|
277
|
+
const federation = input.federation || workerFederation ? createFederationScope(input.federation, false) : void 0;
|
|
248
278
|
if (hydration) {
|
|
249
279
|
const { document, documentId } = hydration;
|
|
250
280
|
bridge = bootstrapStreamedSignalHydration({
|
|
@@ -259,16 +289,34 @@ async function startApplication(input, release, hydration) {
|
|
|
259
289
|
const application = loaded.application;
|
|
260
290
|
releaseModule = loaded.releaseModule;
|
|
261
291
|
input.signal?.throwIfAborted();
|
|
262
|
-
if (bridge) root = hydrateRoot(input.container, federation ? FederationApplication : application.default,
|
|
292
|
+
if (bridge) root = hydrateRoot(input.container, workerFederation ? WorkerFederationApplication : federation ? FederationApplication : application.default, workerFederation ? {
|
|
293
|
+
scope: workerFederation,
|
|
294
|
+
federation,
|
|
295
|
+
application
|
|
296
|
+
} : federation ? {
|
|
263
297
|
scope: federation,
|
|
264
298
|
application
|
|
265
299
|
} : application.props, {
|
|
266
300
|
...errors.options,
|
|
267
|
-
signalOwner: bridge.signalOwner
|
|
301
|
+
signalOwner: bridge.signalOwner,
|
|
302
|
+
externalSnapshots: {
|
|
303
|
+
documentId: hydration.documentId
|
|
304
|
+
}
|
|
268
305
|
});
|
|
269
306
|
else {
|
|
270
|
-
root = createRoot(input.container,
|
|
271
|
-
|
|
307
|
+
root = createRoot(input.container, {
|
|
308
|
+
...errors.options,
|
|
309
|
+
...input.documentId ? {
|
|
310
|
+
externalSnapshots: {
|
|
311
|
+
documentId: input.documentId
|
|
312
|
+
}
|
|
313
|
+
} : {}
|
|
314
|
+
});
|
|
315
|
+
workerFederation ? root.render(WorkerFederationApplication, {
|
|
316
|
+
scope: workerFederation,
|
|
317
|
+
federation,
|
|
318
|
+
application
|
|
319
|
+
}) : federation ? root.render(FederationApplication, {
|
|
272
320
|
scope: federation,
|
|
273
321
|
application
|
|
274
322
|
}) : root.render(application.default, application.props);
|
|
@@ -282,6 +330,8 @@ async function startApplication(input, release, hydration) {
|
|
|
282
330
|
release,
|
|
283
331
|
bridge,
|
|
284
332
|
federation,
|
|
333
|
+
workerFederation,
|
|
334
|
+
retireWorkerFederation,
|
|
285
335
|
...void 0 === input.signal ? {} : {
|
|
286
336
|
signal: input.signal
|
|
287
337
|
},
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createMemoryHistory, createRouter } from "@octanejs/tanstack-router";
|
|
2
2
|
import { createElement } from "octane";
|
|
3
3
|
import { I18nProvider } from "./i18n/I18nProvider.mjs";
|
|
4
|
+
import { associateOctaneFileRouteSnapshot } from "./router-external-snapshot.mjs";
|
|
4
5
|
import { createFileSystemRouteTree } from "./routes.mjs";
|
|
5
6
|
function componentView(component, i18n) {
|
|
6
7
|
const App = component;
|
|
@@ -14,7 +15,8 @@ function componentView(component, i18n) {
|
|
|
14
15
|
}
|
|
15
16
|
const octaneRouterFactory = {
|
|
16
17
|
routeTree: (routes, modules, options)=>createFileSystemRouteTree(routes, modules, options),
|
|
17
|
-
router: ({ location, ...options })=>
|
|
18
|
+
router: ({ location, ...options })=>{
|
|
19
|
+
const router = createRouter({
|
|
18
20
|
...options,
|
|
19
21
|
...location ? {
|
|
20
22
|
origin: location.origin,
|
|
@@ -24,7 +26,10 @@ const octaneRouterFactory = {
|
|
|
24
26
|
]
|
|
25
27
|
})
|
|
26
28
|
} : {}
|
|
27
|
-
})
|
|
29
|
+
});
|
|
30
|
+
associateOctaneFileRouteSnapshot(router);
|
|
31
|
+
return router;
|
|
32
|
+
}
|
|
28
33
|
};
|
|
29
34
|
function localizedRouterRoot(Root) {
|
|
30
35
|
return ({ router, i18n })=>createElement(I18nProvider, {
|
|
@@ -9,6 +9,7 @@ function startNativeClient(options) {
|
|
|
9
9
|
startNativeClientEntry(options, {
|
|
10
10
|
readBootstrap: (document, identity)=>readOctaneDocumentBootstrap(document, identity, nativeHydrationBuildId),
|
|
11
11
|
createRouter: (application, routerOptions)=>createNativeRouter(application, routerOptions, octaneRouterFactory),
|
|
12
|
+
disposeRouter: (router)=>router.history.destroy(),
|
|
12
13
|
async prepareRouter (router, hydrating, signal) {
|
|
13
14
|
if (hydrating) await prepareOctaneRouterHydration(router, {
|
|
14
15
|
signal
|
|
@@ -21,6 +22,8 @@ function startNativeClient(options) {
|
|
|
21
22
|
identity,
|
|
22
23
|
nativeHydrationBuildId,
|
|
23
24
|
federation: options.federation,
|
|
25
|
+
workerFederation: options.workerFederation,
|
|
26
|
+
documentId: bootstrap?.documentId,
|
|
24
27
|
signal,
|
|
25
28
|
load: async ()=>{
|
|
26
29
|
const view = await load();
|
|
@@ -3,7 +3,7 @@ import { createNativeServerEntry } from "@modern-js/renderer-core/entry-server";
|
|
|
3
3
|
import { createNativeRouter } from "@modern-js/renderer-core/router";
|
|
4
4
|
import { componentView, localizedRouterRoot, octaneRouterFactory } from "./entry-application.mjs";
|
|
5
5
|
import { validateOctaneModuleManifest } from "./manifest.mjs";
|
|
6
|
-
import { OctaneRouterServer, createOctaneRequestHandler,
|
|
6
|
+
import { OctaneRouterServer, createOctaneRequestHandler, createSsrStreamResponse, getSsrStatus } from "./router-server.mjs";
|
|
7
7
|
import { renderOctaneApplication, renderOctaneCSRDocument } from "./server.mjs";
|
|
8
8
|
const OCTANE_DOCUMENT_FIELDS = new Set([
|
|
9
9
|
'content-language',
|
|
@@ -34,13 +34,15 @@ function entry_server_createNativeServerEntry(options) {
|
|
|
34
34
|
session,
|
|
35
35
|
document
|
|
36
36
|
}),
|
|
37
|
-
renderComponent: ({ session }, component, document, i18n)=>renderOctaneApplication({
|
|
37
|
+
renderComponent: ({ session, workerFederation }, component, document, i18n)=>renderOctaneApplication({
|
|
38
38
|
session,
|
|
39
39
|
App: componentView(component, i18n),
|
|
40
40
|
document,
|
|
41
|
-
federation: options.federation
|
|
41
|
+
federation: options.federation,
|
|
42
|
+
workerFederation,
|
|
43
|
+
workerFederationLocale: options.workerFederationLocale
|
|
42
44
|
}),
|
|
43
|
-
async renderRoutes ({ request, context: { session }, router, outcomes, document, forbiddenValues, i18n }) {
|
|
45
|
+
async renderRoutes ({ request, context: { session, workerFederation }, router, outcomes, document, forbiddenValues, i18n }) {
|
|
44
46
|
const response = await createOctaneRequestHandler({
|
|
45
47
|
request,
|
|
46
48
|
session,
|
|
@@ -50,7 +52,7 @@ function entry_server_createNativeServerEntry(options) {
|
|
|
50
52
|
}
|
|
51
53
|
})(async ({ router, responseHeaders })=>{
|
|
52
54
|
const metadata = mergeDataResponseMetadata(outcomes, {
|
|
53
|
-
status: router
|
|
55
|
+
status: getSsrStatus(router)
|
|
54
56
|
});
|
|
55
57
|
const policy = dataMetadataToDocumentPolicy({
|
|
56
58
|
...metadata,
|
|
@@ -73,7 +75,9 @@ function entry_server_createNativeServerEntry(options) {
|
|
|
73
75
|
},
|
|
74
76
|
document,
|
|
75
77
|
federation: options.federation,
|
|
76
|
-
|
|
78
|
+
workerFederation,
|
|
79
|
+
workerFederationLocale: options.workerFederationLocale,
|
|
80
|
+
router
|
|
77
81
|
});
|
|
78
82
|
return createSsrStreamResponse(router, rendered);
|
|
79
83
|
});
|
|
@@ -83,4 +87,5 @@ function entry_server_createNativeServerEntry(options) {
|
|
|
83
87
|
}
|
|
84
88
|
});
|
|
85
89
|
}
|
|
90
|
+
export { createNativeWorkerFederationRuntime } from "./worker-federation.mjs";
|
|
86
91
|
export { entry_server_createNativeServerEntry as createNativeServerEntry, withOctaneDocumentFields };
|
package/dist/esm/federation.mjs
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
import { loadFederatedModule
|
|
2
|
-
import { Suspense, createElement, lazy, preinit, useContext } from "octane";
|
|
1
|
+
import { loadFederatedModule } from "@modern-js/renderer-core/federation";
|
|
2
|
+
import { Suspense, createElement, decodeExternalSnapshot, externalSnapshotBoundary, hookSlots, lazy, preinit, publisherBoundary, serializeExternalSnapshotRequest, useContext, useEffect } from "octane";
|
|
3
3
|
import { FederationContext } from "./federation-context.mjs";
|
|
4
|
+
import { I18nContext } from "./i18n/context.mjs";
|
|
5
|
+
import { WorkerFederationContext } from "./worker-federation-context.mjs";
|
|
6
|
+
import { useOctaneWorkerContextKeys } from "./worker-federation-contexts.mjs";
|
|
4
7
|
function withTimeout(promise, timeout, label) {
|
|
5
8
|
if (void 0 === timeout) return promise;
|
|
6
9
|
return new Promise((resolve, reject)=>{
|
|
@@ -15,6 +18,7 @@ function withTimeout(promise, timeout, label) {
|
|
|
15
18
|
});
|
|
16
19
|
});
|
|
17
20
|
}
|
|
21
|
+
const publisherRegistrationEffectSlot = Symbol(hookSlots(1));
|
|
18
22
|
function federatedComponent(remote, options = {}) {
|
|
19
23
|
if ('string' == typeof remote ? !/^[^/]+\/.+/u.test(remote) : 'function' != typeof remote) throw new TypeError("federatedComponent requires a remote id such as 'remote/Widget' or a loader");
|
|
20
24
|
if (void 0 !== options.timeout && (!Number.isFinite(options.timeout) || options.timeout <= 0)) throw new TypeError('federatedComponent timeout must be a positive number of milliseconds');
|
|
@@ -24,19 +28,117 @@ function federatedComponent(remote, options = {}) {
|
|
|
24
28
|
server: "u" < typeof document
|
|
25
29
|
};
|
|
26
30
|
const components = new WeakMap();
|
|
31
|
+
const workerOwners = new WeakMap();
|
|
27
32
|
return (props)=>{
|
|
28
33
|
const context = useContext(FederationContext) ?? standalone;
|
|
29
|
-
|
|
34
|
+
const worker = useContext(WorkerFederationContext);
|
|
35
|
+
const language = worker ? useContext(I18nContext)?.language ?? worker.locale : void 0;
|
|
36
|
+
const contextKeys = worker ? useOctaneWorkerContextKeys(options.contextKeys) : void 0;
|
|
37
|
+
let owner = context;
|
|
38
|
+
if (worker) {
|
|
39
|
+
let scopes = workerOwners.get(context);
|
|
40
|
+
if (!scopes) workerOwners.set(context, scopes = new WeakMap());
|
|
41
|
+
let variants = scopes.get(worker);
|
|
42
|
+
if (!variants) scopes.set(worker, variants = new Map());
|
|
43
|
+
const key = JSON.stringify([
|
|
44
|
+
language,
|
|
45
|
+
contextKeys
|
|
46
|
+
]);
|
|
47
|
+
let variant = variants.get(key);
|
|
48
|
+
if (!variant) variants.set(key, variant = {});
|
|
49
|
+
owner = variant;
|
|
50
|
+
}
|
|
51
|
+
let Remote = components.get(owner);
|
|
30
52
|
if (!Remote) {
|
|
31
53
|
Remote = lazy(async ()=>{
|
|
32
54
|
try {
|
|
33
55
|
if (context.server && !id) throw new Error('A federatedComponent loader renders in the browser only; use a remote id to server-render it');
|
|
34
|
-
|
|
56
|
+
if (worker) {
|
|
57
|
+
if (!id || !language) throw new Error('Worker federation requires a remote id and the owning entry locale');
|
|
58
|
+
const separator = id.indexOf('/');
|
|
59
|
+
const remoteName = id.slice(0, separator);
|
|
60
|
+
const expose = `./${id.slice(separator + 1)}`;
|
|
61
|
+
const controller = new AbortController();
|
|
62
|
+
const signal = worker.signal ? AbortSignal.any([
|
|
63
|
+
worker.signal,
|
|
64
|
+
controller.signal
|
|
65
|
+
]) : controller.signal;
|
|
66
|
+
const timeout = options.timeout ?? (context.server ? 3000 : void 0);
|
|
67
|
+
let admitted;
|
|
68
|
+
try {
|
|
69
|
+
admitted = await withTimeout(worker.transport.admit({
|
|
70
|
+
remote: remoteName,
|
|
71
|
+
expose,
|
|
72
|
+
locale: language,
|
|
73
|
+
signal
|
|
74
|
+
}), timeout, id);
|
|
75
|
+
} finally{
|
|
76
|
+
controller.abort();
|
|
77
|
+
}
|
|
78
|
+
const BrowserRemote = lazy(async ()=>{
|
|
79
|
+
const { module, assets } = await loadFederatedModule(context.instance, id);
|
|
80
|
+
if ('function' != typeof module?.default) throw new TypeError('A federated Octane module must default-export a native component');
|
|
81
|
+
const component = module.default;
|
|
82
|
+
const Loaded = (props, scope, extra)=>{
|
|
83
|
+
for (const href of assets?.css ?? [])preinit(href, {
|
|
84
|
+
as: 'style',
|
|
85
|
+
precedence: 'federated',
|
|
86
|
+
...void 0 === context.nonce ? {} : {
|
|
87
|
+
nonce: context.nonce
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
return component(props, scope, extra);
|
|
91
|
+
};
|
|
92
|
+
return {
|
|
93
|
+
default: Loaded
|
|
94
|
+
};
|
|
95
|
+
});
|
|
96
|
+
const boundary = externalSnapshotBoundary({
|
|
97
|
+
authority: admitted.coordinate.native,
|
|
98
|
+
component: BrowserRemote,
|
|
99
|
+
contextKeys,
|
|
100
|
+
timeoutMs: timeout,
|
|
101
|
+
async snapshot (request, signal) {
|
|
102
|
+
const response = await worker.transport.request({
|
|
103
|
+
remote: remoteName,
|
|
104
|
+
expose,
|
|
105
|
+
locale: language,
|
|
106
|
+
payload: serializeExternalSnapshotRequest(request),
|
|
107
|
+
signal
|
|
108
|
+
});
|
|
109
|
+
return decodeExternalSnapshot(response.payload);
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
const Loaded = (props)=>{
|
|
113
|
+
for (const href of admitted.styles)preinit(href, {
|
|
114
|
+
as: 'style',
|
|
115
|
+
precedence: 'federated',
|
|
116
|
+
...void 0 === context.nonce ? {} : {
|
|
117
|
+
nonce: context.nonce
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
return createElement(boundary, props);
|
|
121
|
+
};
|
|
122
|
+
return {
|
|
123
|
+
default: Loaded
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
const { module, assets } = await withTimeout(Promise.resolve().then(()=>id ? loadFederatedModule(context.instance, id) : remote().then((module)=>({
|
|
127
|
+
module,
|
|
128
|
+
assets: void 0
|
|
129
|
+
}))), options.timeout ?? (context.server ? 3000 : void 0), id ?? 'loader');
|
|
35
130
|
if ('function' != typeof module?.default) throw new TypeError('A federated Octane module must default-export a native component');
|
|
36
|
-
const assets = id && context.instance ? remoteBrowserAssets(context.instance, id) : void 0;
|
|
37
131
|
if (context.server && !assets) throw new Error(`Cannot server-render ${id}: the host has no browser assets for this remote`);
|
|
38
|
-
const component = module.default
|
|
132
|
+
const component = publisherBoundary(module.default, {
|
|
133
|
+
publisherKey: id ?? 'ultramodern:browser-loader'
|
|
134
|
+
});
|
|
39
135
|
const Loaded = (props)=>{
|
|
136
|
+
useEffect(()=>{
|
|
137
|
+
if (context.server || !id || !context.instance) return;
|
|
138
|
+
const register = Reflect.get(context.instance, Symbol.for('ultramodern.federation.browser-dev-boundary.v1'));
|
|
139
|
+
if ('function' != typeof register) return;
|
|
140
|
+
return register(id, module.default, component);
|
|
141
|
+
}, [], publisherRegistrationEffectSlot);
|
|
40
142
|
for (const href of assets?.css ?? [])preinit(href, {
|
|
41
143
|
as: 'style',
|
|
42
144
|
precedence: 'federated',
|
|
@@ -50,11 +152,11 @@ function federatedComponent(remote, options = {}) {
|
|
|
50
152
|
default: Loaded
|
|
51
153
|
};
|
|
52
154
|
} catch (error) {
|
|
53
|
-
if (!context.server) components.delete(
|
|
155
|
+
if (!context.server) components.delete(owner);
|
|
54
156
|
throw error;
|
|
55
157
|
}
|
|
56
158
|
});
|
|
57
|
-
components.set(
|
|
159
|
+
components.set(owner, Remote);
|
|
58
160
|
}
|
|
59
161
|
return createElement(Suspense, {
|
|
60
162
|
fallback: options.fallback,
|
|
@@ -1,20 +1,12 @@
|
|
|
1
|
-
import { createElement, hookSlots,
|
|
1
|
+
import { createElement, hookSlots, useSyncExternalStore } from "octane";
|
|
2
2
|
import { I18nContext } from "./context.mjs";
|
|
3
|
-
const
|
|
4
|
-
const languageSubscriptionSlot = Symbol(hookSlots(1));
|
|
3
|
+
const languageStoreSlot = Symbol(hookSlots(1));
|
|
5
4
|
function I18nProvider(props) {
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
instance.on?.('languageChanged', handleLanguageChanged);
|
|
12
|
-
return ()=>{
|
|
13
|
-
instance.off?.('languageChanged', handleLanguageChanged);
|
|
14
|
-
};
|
|
15
|
-
}, [
|
|
16
|
-
props.instance
|
|
17
|
-
], languageSubscriptionSlot);
|
|
5
|
+
const instance = props.instance;
|
|
6
|
+
const language = useSyncExternalStore((notify)=>{
|
|
7
|
+
instance.on?.('languageChanged', notify);
|
|
8
|
+
return ()=>instance.off?.('languageChanged', notify);
|
|
9
|
+
}, ()=>instance.language, ()=>instance.language, languageStoreSlot);
|
|
18
10
|
const value = {
|
|
19
11
|
instance: props.instance,
|
|
20
12
|
languages: props.languages,
|