@bleedingdev/modern-js-renderer-octane 3.9.3-ultramodern.8 → 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 +11 -3
- package/dist/cjs/federation.js +108 -6
- 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/plugin/index.js +1 -1
- package/dist/cjs/plugin/profile.js +5 -4
- package/dist/cjs/router-external-snapshot.js +263 -0
- package/dist/cjs/server.js +15 -2
- 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 +8 -3
- package/dist/esm/federation.mjs +110 -8
- 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/router-external-snapshot.mjs +216 -0
- package/dist/esm/server.mjs +15 -2
- 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/plugin/index.mjs +1 -1
- package/dist/esm-node/plugin/profile.mjs +5 -4
- 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/router-external-snapshot.d.ts +10 -0
- package/dist/types/server.d.ts +3 -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 +69 -12
- 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 +17 -6
- package/src/federation.ts +159 -15
- 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/plugin/compiler-manifest.cjs +27 -1
- package/src/plugin/index.ts +1 -1
- package/src/plugin/profile.ts +6 -4
- package/src/router-external-snapshot.ts +306 -0
- package/src/server.ts +29 -4
- package/src/worker-federation-context.ts +51 -0
- package/src/worker-federation-contexts.ts +91 -0
- package/src/worker-federation.ts +120 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { NativeLocationRewrite } from '@modern-js/renderer-core/router';
|
|
2
|
+
import { type Context, type ExternalSnapshotContextCodec } from 'octane';
|
|
3
|
+
export declare const OCTANE_WORKER_CONTEXT_KEYS: Readonly<{
|
|
4
|
+
readonly router: 'ultramodern.octane.router-context.v1';
|
|
5
|
+
readonly match: 'ultramodern.octane.match-context.v1';
|
|
6
|
+
readonly i18n: 'ultramodern.octane.i18n-context.v1';
|
|
7
|
+
}>;
|
|
8
|
+
type OctaneWorkerContextKey = (typeof OCTANE_WORKER_CONTEXT_KEYS)[keyof typeof OCTANE_WORKER_CONTEXT_KEYS];
|
|
9
|
+
export interface OctaneWorkerContextCodec<T> extends ExternalSnapshotContextCodec<T> {
|
|
10
|
+
readonly key: OctaneWorkerContextKey;
|
|
11
|
+
readonly dispose?: (value: T) => void | PromiseLike<void>;
|
|
12
|
+
readonly isProvided: (value: T) => boolean;
|
|
13
|
+
}
|
|
14
|
+
/** Optional renderer modules register their actual native context identities. */
|
|
15
|
+
export declare function registerOctaneWorkerContext<T>(context: Context<T>, codec: OctaneWorkerContextCodec<T>): () => void;
|
|
16
|
+
/** Read live providers on every render; explicit requirements retain native validation. */
|
|
17
|
+
export declare function useOctaneWorkerContextKeys(explicit?: readonly string[]): readonly string[];
|
|
18
|
+
export interface OctaneWorkerRouterRewrite {
|
|
19
|
+
readonly capture: (rewrite: NativeLocationRewrite) => unknown;
|
|
20
|
+
readonly restore: (descriptor: unknown) => NativeLocationRewrite;
|
|
21
|
+
}
|
|
22
|
+
/** The optional i18n owner supplies its own native rewrite provenance. */
|
|
23
|
+
export declare function registerOctaneWorkerRouterRewrite(codec: OctaneWorkerRouterRewrite): () => void;
|
|
24
|
+
export declare function getOctaneWorkerRouterRewrite(): OctaneWorkerRouterRewrite | undefined;
|
|
25
|
+
export {};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { NativeWorkerDispatchOptions } from '@modern-js/renderer-core/server';
|
|
2
|
+
import { type AdmitWorkerRendererContract, createWorkerFederationHandler } from '@modern-js/renderer-core/worker-federation';
|
|
3
|
+
import { prepareExternalSnapshotRequest } from 'octane/server';
|
|
4
|
+
type NativeSnapshotRequest = Awaited<ReturnType<typeof prepareExternalSnapshotRequest>>;
|
|
5
|
+
type HandlerOptions = Parameters<typeof createWorkerFederationHandler<NativeSnapshotRequest>>[0];
|
|
6
|
+
/** The compiled registry pins its renderer publication and literal imports. */
|
|
7
|
+
export interface OctaneWorkerFederationRegistry {
|
|
8
|
+
initialize(options: Omit<HandlerOptions, 'publication' | 'exposes'> & {
|
|
9
|
+
publication: unknown;
|
|
10
|
+
}): (request: Request) => Promise<Response | undefined>;
|
|
11
|
+
}
|
|
12
|
+
/** Created per dispatch; no publisher or native snapshot cache crosses documents. */
|
|
13
|
+
export declare function createNativeWorkerFederationRuntime<Bindings extends object>(options: NativeWorkerDispatchOptions<Bindings>, compiler: {
|
|
14
|
+
readonly admitRendererContract: AdmitWorkerRendererContract;
|
|
15
|
+
readonly registry?: OctaneWorkerFederationRegistry;
|
|
16
|
+
}): {
|
|
17
|
+
transport: import("@modern-js/renderer-core/worker-federation").WorkerFederationTransport | undefined;
|
|
18
|
+
handleRequest: (request: Request) => Promise<Response | undefined>;
|
|
19
|
+
};
|
|
20
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bleedingdev/modern-js-renderer-octane",
|
|
3
|
-
"version": "3.9.3-ultramodern.
|
|
3
|
+
"version": "3.9.3-ultramodern.9",
|
|
4
4
|
"description": "UltraModern native Octane application adapter.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -143,7 +143,37 @@
|
|
|
143
143
|
"default": "./dist/cjs/plugin/index.js"
|
|
144
144
|
}
|
|
145
145
|
},
|
|
146
|
-
"./package.json": "./package.json"
|
|
146
|
+
"./package.json": "./package.json",
|
|
147
|
+
"./router-external-snapshot": {
|
|
148
|
+
"import": {
|
|
149
|
+
"types": "./dist/types/router-external-snapshot.d.ts",
|
|
150
|
+
"default": "./dist/esm/router-external-snapshot.mjs"
|
|
151
|
+
},
|
|
152
|
+
"require": {
|
|
153
|
+
"types": "./dist/types/router-external-snapshot.d.ts",
|
|
154
|
+
"default": "./dist/cjs/router-external-snapshot.js"
|
|
155
|
+
}
|
|
156
|
+
},
|
|
157
|
+
"./i18n-external-snapshot": {
|
|
158
|
+
"import": {
|
|
159
|
+
"types": "./dist/types/i18n-external-snapshot.d.ts",
|
|
160
|
+
"default": "./dist/esm/i18n-external-snapshot.mjs"
|
|
161
|
+
},
|
|
162
|
+
"require": {
|
|
163
|
+
"types": "./dist/types/i18n-external-snapshot.d.ts",
|
|
164
|
+
"default": "./dist/cjs/i18n-external-snapshot.js"
|
|
165
|
+
}
|
|
166
|
+
},
|
|
167
|
+
"./worker-federation-contexts": {
|
|
168
|
+
"import": {
|
|
169
|
+
"types": "./dist/types/worker-federation-contexts.d.ts",
|
|
170
|
+
"default": "./dist/esm/worker-federation-contexts.mjs"
|
|
171
|
+
},
|
|
172
|
+
"require": {
|
|
173
|
+
"types": "./dist/types/worker-federation-contexts.d.ts",
|
|
174
|
+
"default": "./dist/cjs/worker-federation-contexts.js"
|
|
175
|
+
}
|
|
176
|
+
}
|
|
147
177
|
},
|
|
148
178
|
"bin": {
|
|
149
179
|
"octane-tsc": "bin/octane-tsc.mjs"
|
|
@@ -152,7 +182,7 @@
|
|
|
152
182
|
"node": ">=26.12.0"
|
|
153
183
|
},
|
|
154
184
|
"dependencies": {
|
|
155
|
-
"@modern-js/renderer-core": "npm:@bleedingdev/modern-js-renderer-core@3.9.3-ultramodern.
|
|
185
|
+
"@modern-js/renderer-core": "npm:@bleedingdev/modern-js-renderer-core@3.9.3-ultramodern.9",
|
|
156
186
|
"@volar/language-core": "2.4.28",
|
|
157
187
|
"jsonc-parser": "3.3.1",
|
|
158
188
|
"seroval": "1.6.9",
|
|
@@ -160,10 +190,11 @@
|
|
|
160
190
|
"typescript": "7.0.2"
|
|
161
191
|
},
|
|
162
192
|
"peerDependencies": {
|
|
163
|
-
"@modern-js/i18n-runtime-extensions": "3.9.3-ultramodern.
|
|
193
|
+
"@modern-js/i18n-runtime-extensions": "3.9.3-ultramodern.9",
|
|
164
194
|
"@octanejs/rspack-plugin": "0.2.4",
|
|
165
195
|
"@octanejs/tanstack-router": "0.2.10",
|
|
166
196
|
"@rsbuild/core": "2.2.13",
|
|
197
|
+
"@tanstack/router-core": "1.171.38",
|
|
167
198
|
"@tsrx/oxc": "0.20.0",
|
|
168
199
|
"i18next": ">=25.7.4",
|
|
169
200
|
"octane": "0.13.8"
|
|
@@ -192,23 +223,52 @@
|
|
|
192
223
|
}
|
|
193
224
|
},
|
|
194
225
|
"devDependencies": {
|
|
195
|
-
"@modern-js/i18n-runtime-extensions": "npm:@bleedingdev/modern-js-i18n-runtime-extensions@3.9.3-ultramodern.
|
|
196
|
-
"@modern-js/tsconfig": "npm:@bleedingdev/modern-js-tsconfig@3.9.3-ultramodern.
|
|
197
|
-
"@octanejs/rspack-plugin": "https://github.com/BleedingDev/octane/releases/download/ultramodern-octane-
|
|
198
|
-
"@octanejs/tanstack-router": "https://github.com/BleedingDev/octane/releases/download/ultramodern-octane-
|
|
226
|
+
"@modern-js/i18n-runtime-extensions": "npm:@bleedingdev/modern-js-i18n-runtime-extensions@3.9.3-ultramodern.9",
|
|
227
|
+
"@modern-js/tsconfig": "npm:@bleedingdev/modern-js-tsconfig@3.9.3-ultramodern.9",
|
|
228
|
+
"@octanejs/rspack-plugin": "https://github.com/BleedingDev/octane/releases/download/ultramodern-octane-0bf2313bbdfb/octanejs-rspack-plugin-0.2.4.tgz",
|
|
229
|
+
"@octanejs/tanstack-router": "https://github.com/BleedingDev/octane/releases/download/ultramodern-octane-0bf2313bbdfb/octanejs-tanstack-router-0.2.10.tgz",
|
|
199
230
|
"@rsbuild/core": "2.2.13",
|
|
200
231
|
"@rslib/core": "1.0.3",
|
|
201
232
|
"@scripts/rstest-config": "2.66.0",
|
|
233
|
+
"@tanstack/router-core": "https://github.com/BleedingDev/router/releases/download/ultramodern-router-core-585d3233f7f5/tanstack-router-core-1.171.38.tgz",
|
|
202
234
|
"@tsrx/oxc": "0.20.0",
|
|
235
|
+
"@types/jsdom": "27.0.0",
|
|
203
236
|
"@types/node": "^26.6.5",
|
|
204
237
|
"happy-dom": "^21.0.3",
|
|
205
|
-
"
|
|
238
|
+
"i18next": "26.4.6",
|
|
239
|
+
"jsdom": "30.0.1",
|
|
240
|
+
"octane": "https://github.com/BleedingDev/octane/releases/download/ultramodern-octane-0bf2313bbdfb/octane-0.13.8.tgz",
|
|
206
241
|
"typescript": "7.0.2"
|
|
207
242
|
},
|
|
208
243
|
"sideEffects": false,
|
|
209
244
|
"publishConfig": {
|
|
210
245
|
"access": "public"
|
|
211
246
|
},
|
|
247
|
+
"ultramodern": {
|
|
248
|
+
"nativeArtifacts": {
|
|
249
|
+
"octane": {
|
|
250
|
+
"request": "https://github.com/BleedingDev/octane/releases/download/ultramodern-octane-0bf2313bbdfb/octane-0.13.8.tgz",
|
|
251
|
+
"version": "0.13.8",
|
|
252
|
+
"integrity": "sha512-5ReT/LEGpeLyAD1NAObxbcvlaC3yjcF2TNA0ZRT63tnO841r8u66WlSbEdcrQmUnIxl/ECsYrmN5LEampD8Jpg=="
|
|
253
|
+
},
|
|
254
|
+
"@octanejs/tanstack-router": {
|
|
255
|
+
"request": "https://github.com/BleedingDev/octane/releases/download/ultramodern-octane-0bf2313bbdfb/octanejs-tanstack-router-0.2.10.tgz",
|
|
256
|
+
"version": "0.2.10",
|
|
257
|
+
"integrity": "sha512-wdnPhVZSQqZg2DDllUyz4RZufn7I0GEnPKrb/CB21Z8JYKnunAtZn41Dma/+xhg5xvN/USnIg9Eyyf2nbJgxMQ=="
|
|
258
|
+
},
|
|
259
|
+
"@octanejs/rspack-plugin": {
|
|
260
|
+
"request": "https://github.com/BleedingDev/octane/releases/download/ultramodern-octane-0bf2313bbdfb/octanejs-rspack-plugin-0.2.4.tgz",
|
|
261
|
+
"version": "0.2.4",
|
|
262
|
+
"integrity": "sha512-I8m/d/oj3Qh8nG5nqkWD90m8qgNwlPK9oNh5tlVvGNdSYCTLddRG7Og8wpYKFAGxe2D+0zOIhYGarC0ZKZgSzw=="
|
|
263
|
+
},
|
|
264
|
+
"@tanstack/router-core": {
|
|
265
|
+
"request": "https://github.com/BleedingDev/router/releases/download/ultramodern-router-core-585d3233f7f5/tanstack-router-core-1.171.38.tgz",
|
|
266
|
+
"version": "1.171.38",
|
|
267
|
+
"integrity": "sha512-d/aGvcyEVwDh2Cn9MuZM1n1dqTIqkFYi7AHY2Na/6x/a0jVSDVTNVVVY7QEsuKFXktaJF9zjVwetaRm/0MALZg=="
|
|
268
|
+
}
|
|
269
|
+
},
|
|
270
|
+
"sourceName": "@modern-js/renderer-octane"
|
|
271
|
+
},
|
|
212
272
|
"scripts": {
|
|
213
273
|
"build": "rslib build && tsc -p tsconfig.types.json",
|
|
214
274
|
"test": "rstest"
|
|
@@ -216,8 +276,5 @@
|
|
|
216
276
|
"homepage": "https://github.com/ultramodernjs/ultramodern.js#readme",
|
|
217
277
|
"bugs": {
|
|
218
278
|
"url": "https://github.com/ultramodernjs/ultramodern.js/issues"
|
|
219
|
-
},
|
|
220
|
-
"ultramodern": {
|
|
221
|
-
"sourceName": "@modern-js/renderer-octane"
|
|
222
279
|
}
|
|
223
280
|
}
|
package/src/client.ts
CHANGED
|
@@ -24,6 +24,11 @@ import {
|
|
|
24
24
|
FederationApplication,
|
|
25
25
|
type OctaneFederationScope,
|
|
26
26
|
} from './federation-context';
|
|
27
|
+
import {
|
|
28
|
+
type OctaneWorkerFederationFactory,
|
|
29
|
+
type OctaneWorkerFederationScope,
|
|
30
|
+
WorkerFederationApplication,
|
|
31
|
+
} from './worker-federation-context';
|
|
27
32
|
|
|
28
33
|
export type { OctaneDocumentBootstrap } from './bootstrap';
|
|
29
34
|
export { readOctaneDocumentBootstrap } from './bootstrap';
|
|
@@ -50,9 +55,14 @@ export interface OctaneApplicationOptions {
|
|
|
50
55
|
readonly signal?: AbortSignal;
|
|
51
56
|
/** Hydration installs the native signal bridge before invoking this importer. */
|
|
52
57
|
readonly load: () => Promise<OctaneApplicationModule>;
|
|
53
|
-
readonly options?: Omit<RootOptions, 'signalOwner'>;
|
|
58
|
+
readonly options?: Omit<RootOptions, 'signalOwner' | 'externalSnapshots'>;
|
|
54
59
|
/** This browser compilation's native Module Federation runtime. */
|
|
55
60
|
readonly federation?: NativeFederationBinding;
|
|
61
|
+
readonly workerFederation?:
|
|
62
|
+
| OctaneWorkerFederationScope
|
|
63
|
+
| OctaneWorkerFederationFactory;
|
|
64
|
+
/** Trusted document bootstrap identity, also present for a CSR document. */
|
|
65
|
+
readonly documentId?: string;
|
|
56
66
|
}
|
|
57
67
|
|
|
58
68
|
export interface OctaneHydrationOptions extends OctaneApplicationOptions {
|
|
@@ -296,6 +306,8 @@ function createHandle(input: {
|
|
|
296
306
|
signal?: AbortSignal;
|
|
297
307
|
onCleanupError?: (error: unknown) => void;
|
|
298
308
|
federation?: OctaneFederationScope;
|
|
309
|
+
workerFederation?: OctaneWorkerFederationScope;
|
|
310
|
+
retireWorkerFederation?: () => void;
|
|
299
311
|
}): OctaneApplicationHandle {
|
|
300
312
|
let releaseModule = input.releaseModule;
|
|
301
313
|
let disposed = false;
|
|
@@ -310,12 +322,18 @@ function createHandle(input: {
|
|
|
310
322
|
const nextRelease = claimModuleResources(next);
|
|
311
323
|
releaseModule = nextRelease;
|
|
312
324
|
try {
|
|
313
|
-
input.
|
|
314
|
-
? input.root.render(
|
|
315
|
-
scope: input.
|
|
325
|
+
input.workerFederation
|
|
326
|
+
? input.root.render(WorkerFederationApplication, {
|
|
327
|
+
scope: input.workerFederation,
|
|
328
|
+
federation: input.federation,
|
|
316
329
|
application: next,
|
|
317
330
|
})
|
|
318
|
-
: input.
|
|
331
|
+
: input.federation
|
|
332
|
+
? input.root.render(FederationApplication, {
|
|
333
|
+
scope: input.federation,
|
|
334
|
+
application: next,
|
|
335
|
+
})
|
|
336
|
+
: input.root.render(next.default, next.props);
|
|
319
337
|
previousRelease();
|
|
320
338
|
} catch (error) {
|
|
321
339
|
cleanupResources([previousRelease, () => handle.dispose()]);
|
|
@@ -327,6 +345,7 @@ function createHandle(input: {
|
|
|
327
345
|
disposed = true;
|
|
328
346
|
input.signal?.removeEventListener('abort', onAbort);
|
|
329
347
|
cleanupResources([
|
|
348
|
+
() => input.retireWorkerFederation?.(),
|
|
330
349
|
() => input.root.unmount(),
|
|
331
350
|
() => releaseModule(),
|
|
332
351
|
() => input.bridge?.dispose(),
|
|
@@ -356,6 +375,21 @@ async function startApplication(
|
|
|
356
375
|
hydration?: { document: Document; documentId: string },
|
|
357
376
|
): Promise<OctaneApplicationHandle> {
|
|
358
377
|
const errors = observeRootErrors(input.options);
|
|
378
|
+
const workerController = input.workerFederation
|
|
379
|
+
? new AbortController()
|
|
380
|
+
: undefined;
|
|
381
|
+
let workerFederation: OctaneWorkerFederationScope | undefined;
|
|
382
|
+
const retireWorkerFederation = () =>
|
|
383
|
+
cleanupResources([
|
|
384
|
+
() =>
|
|
385
|
+
workerController?.abort(
|
|
386
|
+
new DOMException(
|
|
387
|
+
'The Worker federation document was disposed',
|
|
388
|
+
'AbortError',
|
|
389
|
+
),
|
|
390
|
+
),
|
|
391
|
+
() => workerFederation?.transport.dispose(),
|
|
392
|
+
]);
|
|
359
393
|
let bridge: StreamedSignalHydration | undefined;
|
|
360
394
|
let root: Root | undefined;
|
|
361
395
|
let releaseModule: (() => void) | undefined;
|
|
@@ -369,6 +403,7 @@ async function startApplication(
|
|
|
369
403
|
if (cleaned) return;
|
|
370
404
|
cleaned = true;
|
|
371
405
|
cleanupResources([
|
|
406
|
+
retireWorkerFederation,
|
|
372
407
|
() => root?.unmount(),
|
|
373
408
|
() => releaseModule?.(),
|
|
374
409
|
() => bridge?.dispose(),
|
|
@@ -376,10 +411,30 @@ async function startApplication(
|
|
|
376
411
|
]);
|
|
377
412
|
};
|
|
378
413
|
try {
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
414
|
+
if (input.workerFederation && workerController) {
|
|
415
|
+
const signal = AbortSignal.any([
|
|
416
|
+
workerController.signal,
|
|
417
|
+
...(input.signal ? [input.signal] : []),
|
|
418
|
+
]);
|
|
419
|
+
const scope =
|
|
420
|
+
typeof input.workerFederation === 'function'
|
|
421
|
+
? input.workerFederation(signal)
|
|
422
|
+
: input.workerFederation;
|
|
423
|
+
workerFederation = {
|
|
424
|
+
...scope,
|
|
425
|
+
signal: scope.signal ? AbortSignal.any([signal, scope.signal]) : signal,
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
if (workerFederation && !hydration && !input.documentId)
|
|
429
|
+
throw new Error(
|
|
430
|
+
'Worker federation requires the trusted document identity before application loading',
|
|
431
|
+
);
|
|
432
|
+
// Capture the host before imports and retain Worker SSR's provider ranges
|
|
433
|
+
// even when no regular Module Federation binding is configured.
|
|
434
|
+
const federation =
|
|
435
|
+
input.federation || workerFederation
|
|
436
|
+
? createFederationScope(input.federation, false)
|
|
437
|
+
: undefined;
|
|
383
438
|
if (hydration) {
|
|
384
439
|
const { document, documentId } = hydration;
|
|
385
440
|
bridge = bootstrapStreamedSignalHydration({
|
|
@@ -402,15 +457,41 @@ async function startApplication(
|
|
|
402
457
|
if (bridge) {
|
|
403
458
|
root = hydrateRoot(
|
|
404
459
|
input.container,
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
460
|
+
workerFederation
|
|
461
|
+
? WorkerFederationApplication
|
|
462
|
+
: federation
|
|
463
|
+
? FederationApplication
|
|
464
|
+
: application.default,
|
|
465
|
+
workerFederation
|
|
466
|
+
? { scope: workerFederation, federation, application }
|
|
467
|
+
: federation
|
|
468
|
+
? { scope: federation, application }
|
|
469
|
+
: application.props,
|
|
470
|
+
{
|
|
471
|
+
...errors.options,
|
|
472
|
+
signalOwner: bridge.signalOwner,
|
|
473
|
+
externalSnapshots: { documentId: hydration!.documentId },
|
|
474
|
+
},
|
|
408
475
|
);
|
|
409
476
|
} else {
|
|
410
|
-
root = createRoot(input.container,
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
477
|
+
root = createRoot(input.container, {
|
|
478
|
+
...errors.options,
|
|
479
|
+
...(input.documentId
|
|
480
|
+
? { externalSnapshots: { documentId: input.documentId } }
|
|
481
|
+
: {}),
|
|
482
|
+
});
|
|
483
|
+
workerFederation
|
|
484
|
+
? root.render(WorkerFederationApplication, {
|
|
485
|
+
scope: workerFederation,
|
|
486
|
+
federation,
|
|
487
|
+
application,
|
|
488
|
+
})
|
|
489
|
+
: federation
|
|
490
|
+
? root.render(FederationApplication, {
|
|
491
|
+
scope: federation,
|
|
492
|
+
application,
|
|
493
|
+
})
|
|
494
|
+
: root.render(application.default, application.props);
|
|
414
495
|
}
|
|
415
496
|
errors.assertInitialRender();
|
|
416
497
|
input.signal?.throwIfAborted();
|
|
@@ -421,6 +502,8 @@ async function startApplication(
|
|
|
421
502
|
release,
|
|
422
503
|
bridge,
|
|
423
504
|
federation,
|
|
505
|
+
workerFederation,
|
|
506
|
+
retireWorkerFederation,
|
|
424
507
|
...(input.signal === undefined ? {} : { signal: input.signal }),
|
|
425
508
|
onCleanupError: error => reportCleanupError(input, error),
|
|
426
509
|
});
|
package/src/entry-application.ts
CHANGED
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
import { type ComponentBody, createElement } from 'octane';
|
|
11
11
|
import { I18nProvider } from './i18n/I18nProvider';
|
|
12
12
|
import type { I18nInstanceLike } from './i18n/types';
|
|
13
|
+
import { associateOctaneFileRouteSnapshot } from './router-external-snapshot';
|
|
13
14
|
import {
|
|
14
15
|
createFileSystemRouteTree,
|
|
15
16
|
type FileSystemRouteModule,
|
|
@@ -41,8 +42,8 @@ export const octaneRouterFactory: NativeRouterFactory<AnyRoute, AnyRouter> = {
|
|
|
41
42
|
modules as Readonly<Record<string, FileSystemRouteModule>>,
|
|
42
43
|
options,
|
|
43
44
|
),
|
|
44
|
-
router: ({ location, ...options }) =>
|
|
45
|
-
createRouter({
|
|
45
|
+
router: ({ location, ...options }) => {
|
|
46
|
+
const router = createRouter({
|
|
46
47
|
...options,
|
|
47
48
|
...(location
|
|
48
49
|
? {
|
|
@@ -50,7 +51,10 @@ export const octaneRouterFactory: NativeRouterFactory<AnyRoute, AnyRouter> = {
|
|
|
50
51
|
history: createMemoryHistory({ initialEntries: [location.href] }),
|
|
51
52
|
}
|
|
52
53
|
: {}),
|
|
53
|
-
})
|
|
54
|
+
});
|
|
55
|
+
associateOctaneFileRouteSnapshot(router);
|
|
56
|
+
return router;
|
|
57
|
+
},
|
|
54
58
|
};
|
|
55
59
|
|
|
56
60
|
interface LocalizedRouterProps {
|
package/src/entry-client.ts
CHANGED
|
@@ -21,11 +21,18 @@ import {
|
|
|
21
21
|
OctaneRouterRoot,
|
|
22
22
|
prepareOctaneRouterHydration,
|
|
23
23
|
} from './router-client';
|
|
24
|
+
import type {
|
|
25
|
+
OctaneWorkerFederationFactory,
|
|
26
|
+
OctaneWorkerFederationScope,
|
|
27
|
+
} from './worker-federation-context';
|
|
24
28
|
|
|
25
29
|
export interface OctaneClientEntryOptions
|
|
26
30
|
extends NativeClientEntryOptions<NativeI18nInstance, LocalisedUrlsOption> {
|
|
27
31
|
/** The native client compilation hash, `__webpack_hash__`. */
|
|
28
32
|
readonly nativeHydrationBuildId: string;
|
|
33
|
+
readonly workerFederation?:
|
|
34
|
+
| OctaneWorkerFederationScope
|
|
35
|
+
| OctaneWorkerFederationFactory;
|
|
29
36
|
}
|
|
30
37
|
|
|
31
38
|
const LocalizedRouterRoot = localizedRouterRoot(OctaneRouterRoot);
|
|
@@ -38,6 +45,7 @@ export function startNativeClient(options: OctaneClientEntryOptions): void {
|
|
|
38
45
|
readOctaneDocumentBootstrap(document, identity, nativeHydrationBuildId),
|
|
39
46
|
createRouter: (application, routerOptions) =>
|
|
40
47
|
createNativeRouter(application, routerOptions, octaneRouterFactory),
|
|
48
|
+
disposeRouter: router => router.history.destroy(),
|
|
41
49
|
async prepareRouter(router, hydrating, signal) {
|
|
42
50
|
// Hydration adopts the server's matches; a fresh mount loads its own.
|
|
43
51
|
if (hydrating) await prepareOctaneRouterHydration(router, { signal });
|
|
@@ -49,6 +57,8 @@ export function startNativeClient(options: OctaneClientEntryOptions): void {
|
|
|
49
57
|
identity,
|
|
50
58
|
nativeHydrationBuildId,
|
|
51
59
|
federation: options.federation,
|
|
60
|
+
workerFederation: options.workerFederation,
|
|
61
|
+
documentId: bootstrap?.documentId,
|
|
52
62
|
signal,
|
|
53
63
|
// Hydration installs the native signal bridge before this importer runs.
|
|
54
64
|
load: async (): Promise<OctaneApplicationModule> => {
|
package/src/entry-server.ts
CHANGED
|
@@ -52,10 +52,12 @@ export function withOctaneDocumentFields(
|
|
|
52
52
|
: policy;
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
export
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
55
|
+
export { createNativeWorkerFederationRuntime } from './worker-federation';
|
|
56
|
+
|
|
57
|
+
export interface OctaneServerEntryOptions
|
|
58
|
+
extends NativeServerEntryOptions<NativeI18nInstance, LocalisedUrlsOption> {
|
|
59
|
+
readonly workerFederationLocale?: string;
|
|
60
|
+
}
|
|
59
61
|
|
|
60
62
|
const LocalizedRouterServer = localizedRouterRoot(OctaneRouterServer);
|
|
61
63
|
|
|
@@ -77,16 +79,23 @@ export function createNativeServerEntry(
|
|
|
77
79
|
createNativeRouter(application, routerOptions, octaneRouterFactory),
|
|
78
80
|
renderCSR: ({ session }, document) =>
|
|
79
81
|
renderOctaneCSRDocument({ session, document }),
|
|
80
|
-
renderComponent: (
|
|
82
|
+
renderComponent: (
|
|
83
|
+
{ session, workerFederation },
|
|
84
|
+
component,
|
|
85
|
+
document,
|
|
86
|
+
i18n,
|
|
87
|
+
) =>
|
|
81
88
|
renderOctaneApplication({
|
|
82
89
|
session,
|
|
83
90
|
App: componentView(component, i18n) as ServerRenderNode,
|
|
84
91
|
document,
|
|
85
92
|
federation: options.federation,
|
|
93
|
+
workerFederation,
|
|
94
|
+
workerFederationLocale: options.workerFederationLocale,
|
|
86
95
|
}),
|
|
87
96
|
async renderRoutes({
|
|
88
97
|
request,
|
|
89
|
-
context: { session },
|
|
98
|
+
context: { session, workerFederation },
|
|
90
99
|
router,
|
|
91
100
|
outcomes,
|
|
92
101
|
document,
|
|
@@ -127,6 +136,8 @@ export function createNativeServerEntry(
|
|
|
127
136
|
}),
|
|
128
137
|
document,
|
|
129
138
|
federation: options.federation,
|
|
139
|
+
workerFederation,
|
|
140
|
+
workerFederationLocale: options.workerFederationLocale,
|
|
130
141
|
router,
|
|
131
142
|
});
|
|
132
143
|
return createSsrStreamResponse(router, rendered);
|