@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,216 @@
|
|
|
1
|
+
import { assertDefaultRouterRenderHost, associateRouterRenderProfile, captureRouterRenderSnapshot, createRouterRenderRouteTree, defaultParseSearch, defaultStringifySearch, disposeRouterRenderSnapshot, getRouterRenderProfile, matchContext, registerRouterRenderProfile, restoreRouterRenderSnapshot, routerContext } from "@octanejs/tanstack-router";
|
|
2
|
+
import { OCTANE_WORKER_CONTEXT_KEYS, getOctaneWorkerRouterRewrite, registerOctaneWorkerContext } from "./worker-federation-contexts.mjs";
|
|
3
|
+
const OCTANE_FILE_ROUTE_SNAPSHOT_PROFILE = Object.freeze({
|
|
4
|
+
key: 'ultramodern.file-routes',
|
|
5
|
+
version: '1'
|
|
6
|
+
});
|
|
7
|
+
const origins = new WeakMap();
|
|
8
|
+
let profileRegistered = false;
|
|
9
|
+
function plainRecord(value) {
|
|
10
|
+
return null !== value && 'object' == typeof value && [
|
|
11
|
+
Object.prototype,
|
|
12
|
+
null
|
|
13
|
+
].includes(Object.getPrototypeOf(value));
|
|
14
|
+
}
|
|
15
|
+
function record(value, fields) {
|
|
16
|
+
if (!plainRecord(value)) throw new TypeError('Worker router context requires a plain record');
|
|
17
|
+
for (const key of Reflect.ownKeys(value)){
|
|
18
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
19
|
+
if ('string' != typeof key || fields && !fields.includes(key) || !descriptor || !descriptor.enumerable || !('value' in descriptor)) throw new TypeError('Unknown Worker router context field or accessor');
|
|
20
|
+
}
|
|
21
|
+
return value;
|
|
22
|
+
}
|
|
23
|
+
function nonempty(value) {
|
|
24
|
+
if ('string' != typeof value || !value.trim() || value.length > 1024) throw new TypeError('Invalid Worker router identity');
|
|
25
|
+
return value;
|
|
26
|
+
}
|
|
27
|
+
function publicContext(value) {
|
|
28
|
+
const context = record(value, [
|
|
29
|
+
'ultramodern'
|
|
30
|
+
]);
|
|
31
|
+
const metadata = record(context.ultramodern, [
|
|
32
|
+
'rendererIdentity'
|
|
33
|
+
]);
|
|
34
|
+
const identity = record(metadata.rendererIdentity, [
|
|
35
|
+
'renderer',
|
|
36
|
+
'appId',
|
|
37
|
+
'entryName',
|
|
38
|
+
'protocolVersion',
|
|
39
|
+
'buildId'
|
|
40
|
+
]);
|
|
41
|
+
if ('octane' !== identity.renderer || 1 !== identity.protocolVersion) throw new TypeError('Unsupported Worker router identity');
|
|
42
|
+
return Object.freeze({
|
|
43
|
+
ultramodern: Object.freeze({
|
|
44
|
+
rendererIdentity: Object.freeze({
|
|
45
|
+
renderer: 'octane',
|
|
46
|
+
appId: nonempty(identity.appId),
|
|
47
|
+
entryName: nonempty(identity.entryName),
|
|
48
|
+
protocolVersion: 1,
|
|
49
|
+
buildId: nonempty(identity.buildId)
|
|
50
|
+
})
|
|
51
|
+
})
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
function localizedUrls(value) {
|
|
55
|
+
if (void 0 === value || 'boolean' == typeof value) return value;
|
|
56
|
+
const input = record(value);
|
|
57
|
+
const result = {};
|
|
58
|
+
for (const [path, translations] of Object.entries(input)){
|
|
59
|
+
const names = {};
|
|
60
|
+
for (const [language, target] of Object.entries(record(translations)))names[nonempty(language)] = nonempty(target);
|
|
61
|
+
result[path] = names;
|
|
62
|
+
}
|
|
63
|
+
return result;
|
|
64
|
+
}
|
|
65
|
+
function rewriteDescriptor(value) {
|
|
66
|
+
if (void 0 === value) return;
|
|
67
|
+
const input = record(value, [
|
|
68
|
+
'language',
|
|
69
|
+
'languages',
|
|
70
|
+
'localisedUrls'
|
|
71
|
+
]);
|
|
72
|
+
if (!Array.isArray(input.languages) || !input.languages.length || input.languages.length > 64) throw new TypeError('Invalid Worker router i18n languages');
|
|
73
|
+
const languages = input.languages.map(nonempty);
|
|
74
|
+
const language = nonempty(input.language);
|
|
75
|
+
if (new Set(languages).size !== languages.length || !languages.includes(language)) throw new TypeError('Invalid Worker router i18n language');
|
|
76
|
+
const urls = localizedUrls(input.localisedUrls);
|
|
77
|
+
return {
|
|
78
|
+
language,
|
|
79
|
+
languages,
|
|
80
|
+
...void 0 === urls ? {} : {
|
|
81
|
+
localisedUrls: urls
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
function nativeRewriteDescriptor(router) {
|
|
86
|
+
if (!router.options.rewrite) return;
|
|
87
|
+
const known = getOctaneWorkerRouterRewrite()?.capture(router.options.rewrite);
|
|
88
|
+
if (!known) throw new TypeError('Custom router rewrite requires an exact Worker profile');
|
|
89
|
+
const descriptor = rewriteDescriptor(known);
|
|
90
|
+
if (!descriptor) throw new TypeError('Missing native router rewrite provenance');
|
|
91
|
+
return {
|
|
92
|
+
language: descriptor.language,
|
|
93
|
+
languages: [
|
|
94
|
+
...descriptor.languages
|
|
95
|
+
],
|
|
96
|
+
...void 0 === descriptor.localisedUrls ? {} : {
|
|
97
|
+
localisedUrls: descriptor.localisedUrls
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
function validateDefaultSnapshot(snapshot) {
|
|
102
|
+
publicContext(snapshot.publicContext);
|
|
103
|
+
rewriteDescriptor(snapshot.i18n);
|
|
104
|
+
}
|
|
105
|
+
function ensureDefaultProfile() {
|
|
106
|
+
if (profileRegistered) return;
|
|
107
|
+
registerRouterRenderProfile({
|
|
108
|
+
id: OCTANE_FILE_ROUTE_SNAPSHOT_PROFILE,
|
|
109
|
+
assertHost: (router, snapshot)=>{
|
|
110
|
+
const origin = origins.get(router);
|
|
111
|
+
if (!origin || origin.rewrite !== router.options.rewrite || JSON.stringify(publicContext(router.options.context)) !== JSON.stringify(origin.publicContext)) throw new TypeError('Generated Worker router origin changed');
|
|
112
|
+
assertDefaultRouterRenderHost(router, void 0 !== snapshot.i18n);
|
|
113
|
+
validateDefaultSnapshot(snapshot);
|
|
114
|
+
if (JSON.stringify(nativeRewriteDescriptor(router)) !== JSON.stringify(snapshot.i18n)) throw new TypeError('Worker router rewrite differs from its native provenance');
|
|
115
|
+
},
|
|
116
|
+
createRoutingTree: (snapshot)=>{
|
|
117
|
+
validateDefaultSnapshot(snapshot);
|
|
118
|
+
return createRouterRenderRouteTree(snapshot.routes);
|
|
119
|
+
},
|
|
120
|
+
routingOptions: (snapshot)=>{
|
|
121
|
+
const defaults = {
|
|
122
|
+
parseSearch: defaultParseSearch,
|
|
123
|
+
stringifySearch: defaultStringifySearch
|
|
124
|
+
};
|
|
125
|
+
const i18n = rewriteDescriptor(snapshot.i18n);
|
|
126
|
+
if (!i18n) return defaults;
|
|
127
|
+
const codec = getOctaneWorkerRouterRewrite();
|
|
128
|
+
if (!codec) throw new TypeError('Missing owning Worker router i18n codec');
|
|
129
|
+
return {
|
|
130
|
+
...defaults,
|
|
131
|
+
rewrite: codec.restore(i18n)
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
profileRegistered = true;
|
|
136
|
+
}
|
|
137
|
+
function associateOctaneFileRouteSnapshot(router) {
|
|
138
|
+
ensureDefaultProfile();
|
|
139
|
+
const context = publicContext(router.options.context);
|
|
140
|
+
origins.set(router, {
|
|
141
|
+
publicContext: context,
|
|
142
|
+
rewrite: router.options.rewrite
|
|
143
|
+
});
|
|
144
|
+
associateRouterRenderProfile(router, {
|
|
145
|
+
id: OCTANE_FILE_ROUTE_SNAPSHOT_PROFILE,
|
|
146
|
+
publicContext: context
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
function captureOctaneRouterSnapshot(router) {
|
|
150
|
+
const origin = origins.get(router);
|
|
151
|
+
const declared = getRouterRenderProfile(router);
|
|
152
|
+
if (origin && declared?.key === OCTANE_FILE_ROUTE_SNAPSHOT_PROFILE.key && declared.version === OCTANE_FILE_ROUTE_SNAPSHOT_PROFILE.version) {
|
|
153
|
+
const i18n = nativeRewriteDescriptor(router);
|
|
154
|
+
associateRouterRenderProfile(router, {
|
|
155
|
+
id: OCTANE_FILE_ROUTE_SNAPSHOT_PROFILE,
|
|
156
|
+
publicContext: origin.publicContext,
|
|
157
|
+
...void 0 === i18n ? {} : {
|
|
158
|
+
i18n
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
return captureRouterRenderSnapshot(router);
|
|
163
|
+
}
|
|
164
|
+
let registrations = 0;
|
|
165
|
+
let releaseContexts;
|
|
166
|
+
function registerOctaneRouterWorkerContexts() {
|
|
167
|
+
ensureDefaultProfile();
|
|
168
|
+
if (!releaseContexts) {
|
|
169
|
+
const releaseRouter = registerOctaneWorkerContext(routerContext, {
|
|
170
|
+
key: OCTANE_WORKER_CONTEXT_KEYS.router,
|
|
171
|
+
encode: (value)=>{
|
|
172
|
+
if (!value) throw new TypeError('Missing native router provider');
|
|
173
|
+
return captureOctaneRouterSnapshot(value);
|
|
174
|
+
},
|
|
175
|
+
decode: (value, signal)=>restoreRouterRenderSnapshot(value, {
|
|
176
|
+
signal
|
|
177
|
+
}),
|
|
178
|
+
dispose: (value)=>{
|
|
179
|
+
if (value) disposeRouterRenderSnapshot(value);
|
|
180
|
+
},
|
|
181
|
+
isProvided: (value)=>void 0 !== value
|
|
182
|
+
});
|
|
183
|
+
try {
|
|
184
|
+
const releaseMatch = registerOctaneWorkerContext(matchContext, {
|
|
185
|
+
key: OCTANE_WORKER_CONTEXT_KEYS.match,
|
|
186
|
+
encode: nonempty,
|
|
187
|
+
decode: nonempty,
|
|
188
|
+
validate: (value, readContext)=>{
|
|
189
|
+
const selected = readContext(routerContext);
|
|
190
|
+
const router = selected.present ? selected.value : void 0;
|
|
191
|
+
const current = router?.state.matches.find((match)=>match.routeId === value);
|
|
192
|
+
if (!router || !current || 'string' != typeof value || router.stores.getMatchStore(value).get() !== current) throw new TypeError('Worker match context requires its selected native router and a current match');
|
|
193
|
+
},
|
|
194
|
+
isProvided: (value)=>void 0 !== value
|
|
195
|
+
});
|
|
196
|
+
releaseContexts = ()=>{
|
|
197
|
+
releaseMatch();
|
|
198
|
+
releaseRouter();
|
|
199
|
+
};
|
|
200
|
+
} catch (error) {
|
|
201
|
+
releaseRouter();
|
|
202
|
+
throw error;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
registrations++;
|
|
206
|
+
let released = false;
|
|
207
|
+
return ()=>{
|
|
208
|
+
if (released) return;
|
|
209
|
+
released = true;
|
|
210
|
+
if (0 === --registrations) {
|
|
211
|
+
releaseContexts?.();
|
|
212
|
+
releaseContexts = void 0;
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
export { OCTANE_FILE_ROUTE_SNAPSHOT_PROFILE, associateOctaneFileRouteSnapshot, captureOctaneRouterSnapshot, registerOctaneRouterWorkerContexts };
|
package/dist/esm/server.mjs
CHANGED
|
@@ -4,6 +4,7 @@ import { transformReadableStreamWithRouter } from "@octanejs/tanstack-router/ssr
|
|
|
4
4
|
import { createElement, earlySignalBootstrapScript, renderToReadableStream } from "octane/server";
|
|
5
5
|
import { assertNativeHydrationBuildId, assertOctaneIdentity } from "./bootstrap.mjs";
|
|
6
6
|
import { FederationRoot, createFederationScope } from "./federation-context.mjs";
|
|
7
|
+
import { WorkerFederationRoot } from "./worker-federation-context.mjs";
|
|
7
8
|
function prepareOctaneDocument(session, document, hydrating) {
|
|
8
9
|
assertOctaneIdentity(session.identity);
|
|
9
10
|
if ('node' !== session.platform.kind && 'worker' !== session.platform.kind) throw new Error('An Octane document requires a Node or worker request platform.');
|
|
@@ -74,12 +75,21 @@ async function renderOctaneApplication(input) {
|
|
|
74
75
|
if (session.responsePolicy?.kind === 'terminal') return session.respond(null);
|
|
75
76
|
session.startRendering();
|
|
76
77
|
let head = '';
|
|
77
|
-
const federation = input.federation ? createFederationScope(input.federation, true, document.nonce) : void 0;
|
|
78
|
+
const federation = input.federation || input.workerFederation ? createFederationScope(input.federation, true, document.nonce) : void 0;
|
|
78
79
|
const App = federation ? ()=>createElement(FederationRoot, {
|
|
79
80
|
scope: federation,
|
|
80
81
|
children: 'function' == typeof input.App ? createElement(input.App, input.props) : input.App
|
|
81
82
|
}) : input.App;
|
|
82
|
-
const
|
|
83
|
+
const workerFederation = input.workerFederation ? {
|
|
84
|
+
transport: input.workerFederation,
|
|
85
|
+
locale: input.workerFederationLocale ?? 'default',
|
|
86
|
+
signal: session.signal
|
|
87
|
+
} : void 0;
|
|
88
|
+
const NativeApp = workerFederation ? ()=>createElement(WorkerFederationRoot, {
|
|
89
|
+
scope: workerFederation,
|
|
90
|
+
children: 'function' == typeof App ? createElement(App, input.props) : App
|
|
91
|
+
}) : App;
|
|
92
|
+
const nativeStream = await renderToReadableStream(NativeApp, input.props, {
|
|
83
93
|
signal: session.signal,
|
|
84
94
|
...void 0 === document.nonce ? {} : {
|
|
85
95
|
nonce: document.nonce
|
|
@@ -92,6 +102,9 @@ async function renderOctaneApplication(input) {
|
|
|
92
102
|
buildId: document.nativeHydrationBuildId,
|
|
93
103
|
documentId: document.documentId
|
|
94
104
|
},
|
|
105
|
+
externalSnapshots: {
|
|
106
|
+
documentId: document.documentId
|
|
107
|
+
},
|
|
95
108
|
headChannel: 'separate',
|
|
96
109
|
onHeadReady (value) {
|
|
97
110
|
head = value;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { createContext, createElement } from "octane";
|
|
2
|
+
import { FederationApplication } from "./federation-context.mjs";
|
|
3
|
+
const WorkerFederationContext = createContext(null);
|
|
4
|
+
function WorkerFederationRoot(props) {
|
|
5
|
+
return createElement(WorkerFederationContext, {
|
|
6
|
+
value: props.scope,
|
|
7
|
+
children: props.children
|
|
8
|
+
});
|
|
9
|
+
}
|
|
10
|
+
function WorkerFederationApplication(props) {
|
|
11
|
+
return createElement(WorkerFederationRoot, {
|
|
12
|
+
scope: props.scope,
|
|
13
|
+
children: props.federation ? createElement(FederationApplication, {
|
|
14
|
+
scope: props.federation,
|
|
15
|
+
application: props.application
|
|
16
|
+
}) : createElement(props.application.default, props.application.props)
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
export { WorkerFederationApplication, WorkerFederationContext, WorkerFederationRoot };
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { registerExternalSnapshotContext, useContext } from "octane";
|
|
2
|
+
const OCTANE_WORKER_CONTEXT_KEYS = Object.freeze({
|
|
3
|
+
router: 'ultramodern.octane.router-context.v1',
|
|
4
|
+
match: 'ultramodern.octane.match-context.v1',
|
|
5
|
+
i18n: 'ultramodern.octane.i18n-context.v1'
|
|
6
|
+
});
|
|
7
|
+
const builtinKeys = Object.values(OCTANE_WORKER_CONTEXT_KEYS);
|
|
8
|
+
const contexts = new Map();
|
|
9
|
+
function registerOctaneWorkerContext(context, codec) {
|
|
10
|
+
const { key, isProvided } = codec;
|
|
11
|
+
if (!builtinKeys.includes(key) || 'function' != typeof isProvided) throw new TypeError('Invalid Octane Worker builtin context codec');
|
|
12
|
+
const unregister = registerExternalSnapshotContext(context, codec);
|
|
13
|
+
const hasProvider = ()=>isProvided(useContext(context));
|
|
14
|
+
contexts.set(key, hasProvider);
|
|
15
|
+
return ()=>{
|
|
16
|
+
if (contexts.get(key) !== hasProvider) return;
|
|
17
|
+
unregister();
|
|
18
|
+
contexts.delete(key);
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
function useOctaneWorkerContextKeys(explicit = []) {
|
|
22
|
+
const selected = [];
|
|
23
|
+
for (const key of builtinKeys){
|
|
24
|
+
const hasProvider = contexts.get(key);
|
|
25
|
+
if (hasProvider?.() && !explicit.includes(key)) selected.push(key);
|
|
26
|
+
}
|
|
27
|
+
return [
|
|
28
|
+
...selected,
|
|
29
|
+
...explicit
|
|
30
|
+
];
|
|
31
|
+
}
|
|
32
|
+
let routerRewrite;
|
|
33
|
+
function registerOctaneWorkerRouterRewrite(codec) {
|
|
34
|
+
if (routerRewrite || 'function' != typeof codec.capture || 'function' != typeof codec.restore) throw new TypeError('Invalid or duplicate Octane Worker router rewrite codec');
|
|
35
|
+
const captured = Object.freeze({
|
|
36
|
+
capture: codec.capture,
|
|
37
|
+
restore: codec.restore
|
|
38
|
+
});
|
|
39
|
+
routerRewrite = captured;
|
|
40
|
+
return ()=>{
|
|
41
|
+
if (routerRewrite === captured) routerRewrite = void 0;
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
function getOctaneWorkerRouterRewrite() {
|
|
45
|
+
return routerRewrite;
|
|
46
|
+
}
|
|
47
|
+
export { OCTANE_WORKER_CONTEXT_KEYS, getOctaneWorkerRouterRewrite, registerOctaneWorkerContext, registerOctaneWorkerRouterRewrite, useOctaneWorkerContextKeys };
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { createWorkerFederationTransport } from "@modern-js/renderer-core/worker-federation";
|
|
2
|
+
import { prepareExternalSnapshotRequest, releasePreparedExternalSnapshotRequest, renderExternalSnapshot, serializeExternalSnapshot } from "octane/server";
|
|
3
|
+
import { FederationContext, createFederationScope } from "./federation-context.mjs";
|
|
4
|
+
import { WorkerFederationContext } from "./worker-federation-context.mjs";
|
|
5
|
+
function isNativeWorkerComponent(value) {
|
|
6
|
+
return 'function' == typeof value;
|
|
7
|
+
}
|
|
8
|
+
function createNativeWorkerFederationRuntime(options, compiler) {
|
|
9
|
+
const { resources } = options;
|
|
10
|
+
const bindings = options.bindings;
|
|
11
|
+
const transport = resources.workerFederationServices?.length ? createWorkerFederationTransport({
|
|
12
|
+
admitRendererContract: compiler.admitRendererContract,
|
|
13
|
+
services: resources.workerFederationServices.map((service)=>{
|
|
14
|
+
const binding = bindings && Reflect.get(bindings, service.binding);
|
|
15
|
+
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}.`);
|
|
16
|
+
return {
|
|
17
|
+
...service,
|
|
18
|
+
fetch: binding.fetch.bind(binding)
|
|
19
|
+
};
|
|
20
|
+
})
|
|
21
|
+
}) : void 0;
|
|
22
|
+
let handle;
|
|
23
|
+
if (resources.workerFederation) {
|
|
24
|
+
if (!compiler.registry || !resources.workerFederationManifestUrl) throw new Error('Native Worker federation requires its compiled expose registry and pinned manifest URL.');
|
|
25
|
+
handle = compiler.registry.initialize({
|
|
26
|
+
publication: resources.workerFederation.worker,
|
|
27
|
+
manifestUrl: resources.workerFederationManifestUrl,
|
|
28
|
+
admitRendererContract: compiler.admitRendererContract,
|
|
29
|
+
admitNativeRequest (payload, { admitted, signal }) {
|
|
30
|
+
const initializeContexts = transport ? (provide, request)=>{
|
|
31
|
+
provide(FederationContext, createFederationScope(void 0, true, request.nonce));
|
|
32
|
+
provide(WorkerFederationContext, {
|
|
33
|
+
transport,
|
|
34
|
+
locale: admitted.locale,
|
|
35
|
+
signal
|
|
36
|
+
});
|
|
37
|
+
} : void 0;
|
|
38
|
+
return prepareExternalSnapshotRequest(payload, {
|
|
39
|
+
authority: admitted.coordinate.native,
|
|
40
|
+
signal,
|
|
41
|
+
...initializeContexts ? {
|
|
42
|
+
initializeContexts
|
|
43
|
+
} : {}
|
|
44
|
+
});
|
|
45
|
+
},
|
|
46
|
+
releaseNativeRequest: releasePreparedExternalSnapshotRequest,
|
|
47
|
+
async renderNativeSnapshot (module, request, { admitted }) {
|
|
48
|
+
const component = module && 'object' == typeof module && 'default' in module ? module.default : void 0;
|
|
49
|
+
if (!isNativeWorkerComponent(component)) throw new TypeError(`Native Worker expose ${admitted.expose} requires a default Octane component.`);
|
|
50
|
+
return serializeExternalSnapshot(await renderExternalSnapshot(component, request));
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
return {
|
|
55
|
+
transport,
|
|
56
|
+
handleRequest: handle ?? (()=>Promise.resolve(void 0))
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
export { createNativeWorkerFederationRuntime };
|
|
@@ -22,7 +22,7 @@ const octaneProfile = {
|
|
|
22
22
|
name: '@octanejs/tanstack-router',
|
|
23
23
|
version: '0.2.10',
|
|
24
24
|
coreName: '@tanstack/router-core',
|
|
25
|
-
coreVersion: '1.171.
|
|
25
|
+
coreVersion: '1.171.38'
|
|
26
26
|
},
|
|
27
27
|
sourceExtensions: [
|
|
28
28
|
'.tsx',
|
|
@@ -33,9 +33,10 @@ const octaneProfile = {
|
|
|
33
33
|
jsxImportSource: 'octane',
|
|
34
34
|
dependencies: {
|
|
35
35
|
'@tsrx/oxc': '0.20.0',
|
|
36
|
-
octane: 'https://github.com/BleedingDev/octane/releases/download/ultramodern-octane-
|
|
37
|
-
'@octanejs/tanstack-router': 'https://github.com/BleedingDev/octane/releases/download/ultramodern-octane-
|
|
38
|
-
'@octanejs/rspack-plugin': 'https://github.com/BleedingDev/octane/releases/download/ultramodern-octane-
|
|
36
|
+
octane: 'https://github.com/BleedingDev/octane/releases/download/ultramodern-octane-0bf2313bbdfb/octane-0.13.8.tgz',
|
|
37
|
+
'@octanejs/tanstack-router': 'https://github.com/BleedingDev/octane/releases/download/ultramodern-octane-0bf2313bbdfb/octanejs-tanstack-router-0.2.10.tgz',
|
|
38
|
+
'@octanejs/rspack-plugin': 'https://github.com/BleedingDev/octane/releases/download/ultramodern-octane-0bf2313bbdfb/octanejs-rspack-plugin-0.2.4.tgz',
|
|
39
|
+
'@tanstack/router-core': 'https://github.com/BleedingDev/router/releases/download/ultramodern-router-core-585d3233f7f5/tanstack-router-core-1.171.38.tgz',
|
|
39
40
|
'@modern-js/renderer-octane': '3.8.3',
|
|
40
41
|
seroval: '1.6.9',
|
|
41
42
|
'seroval-plugins': '1.6.9'
|
package/dist/types/client.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { NativeFederationBinding } from '@modern-js/renderer-core/federation';
|
|
2
2
|
import { type RendererIdentity } from '@modern-js/renderer-core/identity';
|
|
3
3
|
import { type ComponentBody, type Root, type RootContainer, type RootOptions } from 'octane';
|
|
4
|
+
import { type OctaneWorkerFederationFactory, type OctaneWorkerFederationScope } from './worker-federation-context';
|
|
4
5
|
export type { OctaneDocumentBootstrap } from './bootstrap';
|
|
5
6
|
export { readOctaneDocumentBootstrap } from './bootstrap';
|
|
6
7
|
export interface OctaneApplicationModule {
|
|
@@ -23,9 +24,12 @@ export interface OctaneApplicationOptions {
|
|
|
23
24
|
readonly signal?: AbortSignal;
|
|
24
25
|
/** Hydration installs the native signal bridge before invoking this importer. */
|
|
25
26
|
readonly load: () => Promise<OctaneApplicationModule>;
|
|
26
|
-
readonly options?: Omit<RootOptions, 'signalOwner'>;
|
|
27
|
+
readonly options?: Omit<RootOptions, 'signalOwner' | 'externalSnapshots'>;
|
|
27
28
|
/** This browser compilation's native Module Federation runtime. */
|
|
28
29
|
readonly federation?: NativeFederationBinding;
|
|
30
|
+
readonly workerFederation?: OctaneWorkerFederationScope | OctaneWorkerFederationFactory;
|
|
31
|
+
/** Trusted document bootstrap identity, also present for a CSR document. */
|
|
32
|
+
readonly documentId?: string;
|
|
29
33
|
}
|
|
30
34
|
export interface OctaneHydrationOptions extends OctaneApplicationOptions {
|
|
31
35
|
readonly documentIdentity: RendererIdentity;
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import type { LocalisedUrlsOption } from '@modern-js/i18n-runtime-extensions/paths';
|
|
2
2
|
import { type NativeClientEntryOptions, type NativeI18nInstance } from '@modern-js/renderer-core/entry-client';
|
|
3
|
+
import type { OctaneWorkerFederationFactory, OctaneWorkerFederationScope } from './worker-federation-context';
|
|
3
4
|
export interface OctaneClientEntryOptions extends NativeClientEntryOptions<NativeI18nInstance, LocalisedUrlsOption> {
|
|
4
5
|
/** The native client compilation hash, `__webpack_hash__`. */
|
|
5
6
|
readonly nativeHydrationBuildId: string;
|
|
7
|
+
readonly workerFederation?: OctaneWorkerFederationScope | OctaneWorkerFederationFactory;
|
|
6
8
|
}
|
|
7
9
|
/** Start a generated Octane browser entry: mount, or hydrate the server document. */
|
|
8
10
|
export declare function startNativeClient(options: OctaneClientEntryOptions): void;
|
|
@@ -6,6 +6,9 @@ import type { ResponsePolicy } from '@modern-js/renderer-core/session';
|
|
|
6
6
|
* declares for the document itself are kept, as Solid document headers are.
|
|
7
7
|
*/
|
|
8
8
|
export declare function withOctaneDocumentFields(policy: ResponsePolicy, responseHeaders: Headers): ResponsePolicy;
|
|
9
|
-
export
|
|
9
|
+
export { createNativeWorkerFederationRuntime } from './worker-federation';
|
|
10
|
+
export interface OctaneServerEntryOptions extends NativeServerEntryOptions<NativeI18nInstance, LocalisedUrlsOption> {
|
|
11
|
+
readonly workerFederationLocale?: string;
|
|
12
|
+
}
|
|
10
13
|
/** The native transport handlers of a generated Octane server entry. */
|
|
11
14
|
export declare function createNativeServerEntry(options: OctaneServerEntryOptions): NativeServerEntry;
|
|
@@ -8,6 +8,8 @@ export interface FederatedComponentOptions {
|
|
|
8
8
|
readonly fallback?: () => OctaneNode;
|
|
9
9
|
/** A positive load limit; the server defaults to 3000ms. */
|
|
10
10
|
readonly timeout?: number;
|
|
11
|
+
/** Explicit registered server projections required by an application remote. */
|
|
12
|
+
readonly contextKeys?: readonly string[];
|
|
11
13
|
}
|
|
12
14
|
/**
|
|
13
15
|
* Render a native Octane remote by its `remote/Expose` id or a browser loader.
|
|
@@ -1,41 +1,14 @@
|
|
|
1
1
|
import { type OctaneNode } from 'octane';
|
|
2
|
-
import { type LinkComponentProps } from '../router';
|
|
3
|
-
export interface LocalizedLinkProps {
|
|
2
|
+
import { type AnyRouter, type LinkComponentProps } from '../router';
|
|
3
|
+
export interface LocalizedLinkProps extends Omit<LinkComponentProps<'a', AnyRouter, string, string>, 'to' | 'mask' | 'href' | 'publicHref' | '_fromLocation'> {
|
|
4
4
|
/** A canonical, language-agnostic target, e.g. `/products/red-shoe`. */
|
|
5
5
|
to: string;
|
|
6
6
|
/** Localize for a specific language instead of the current one. */
|
|
7
7
|
language?: string;
|
|
8
|
-
children?: unknown;
|
|
9
|
-
class?: string;
|
|
10
|
-
activeProps?: LinkComponentProps<'a'>['activeProps'];
|
|
11
|
-
inactiveProps?: LinkComponentProps<'a'>['inactiveProps'];
|
|
12
|
-
activeOptions?: LinkComponentProps<'a'>['activeOptions'];
|
|
13
|
-
replace?: boolean;
|
|
14
|
-
preload?: LinkComponentProps<'a'>['preload'];
|
|
15
8
|
}
|
|
16
9
|
/**
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
* to the real link — the same contract as `@modern-js/plugin-i18n`'s React
|
|
21
|
-
* `Link`. It resolves the href itself via the shared pathname helpers, so it
|
|
22
|
-
* works with or without the router's i18n `rewrite`. Every link, including
|
|
23
|
-
* one to another language, is the native `Link`: navigation, preloading and
|
|
24
|
-
* active state stay with the router.
|
|
25
|
-
*
|
|
26
|
-
* Under the i18n `rewrite` the router matches canonical paths and localizes
|
|
27
|
-
* outgoing locations to the current language, so a link to another language
|
|
28
|
-
* routes to the canonical `to` and publishes the target language's URL as a
|
|
29
|
-
* route mask (the rewrite keeps a mask's explicit language). The entry's
|
|
30
|
-
* router language synchronization (`syncWithRouter`) switches the i18next
|
|
31
|
-
* instance from the navigated URL, as it does for history navigation.
|
|
32
|
-
*
|
|
33
|
-
* No JSX: built with `createElement`, matching this package's existing
|
|
34
|
-
* style (see `src/routes.ts`).
|
|
10
|
+
* The native router owns client navigation, preloading and active state. Its mask
|
|
11
|
+
* keeps the canonical route while publishing the selected language URL;
|
|
12
|
+
* the entry's i18n router subscription synchronizes the instance on navigation.
|
|
35
13
|
*/
|
|
36
14
|
export declare function LocalizedLink(props: LocalizedLinkProps): OctaneNode;
|
|
37
|
-
/**
|
|
38
|
-
* Language-invariant active check: `to="/products"` is active whether the
|
|
39
|
-
* current location is `/en/products` or `/produkty` under a mapped locale.
|
|
40
|
-
*/
|
|
41
|
-
export declare function useIsLocalizedActive(canonicalTarget: string): boolean;
|
|
@@ -3,7 +3,7 @@ export type { I18nContextValue, I18nInstanceLike } from './types';
|
|
|
3
3
|
/**
|
|
4
4
|
* Octane's `createContext<T>(defaultValue: T)` requires a default (unlike
|
|
5
5
|
* Solid 2's default-less form), so a missing provider is detected by
|
|
6
|
-
* checking for `null` in `useI18n`/`
|
|
6
|
+
* checking for `null` in `useI18n`/`LocalizedLink` instead — the same
|
|
7
7
|
* guard shape `@modern-js/plugin-i18n`'s React `ModernI18nContext` uses.
|
|
8
8
|
*/
|
|
9
9
|
export declare const I18nContext: import("octane").Context<I18nContextValue | null>;
|
|
@@ -3,7 +3,7 @@ export { createI18nSsrHandoffInlineData, I18N_SSR_HANDOFF_ELEMENT_ID, type I18nS
|
|
|
3
3
|
export { type CreateI18nUrlRewriteOptions, createI18nUrlRewrite, type I18nLocationRewrite, } from '@modern-js/i18n-runtime-extensions/urlRewrite';
|
|
4
4
|
export { I18nContext } from './context';
|
|
5
5
|
export { I18nProvider, type I18nProviderProps } from './I18nProvider';
|
|
6
|
-
export { LocalizedLink, type LocalizedLinkProps
|
|
6
|
+
export { LocalizedLink, type LocalizedLinkProps } from './LocalizedLink';
|
|
7
7
|
export { type OctaneLatestLanguageSyncOptions, useLatestLanguageSync, } from './languageSync';
|
|
8
8
|
export type { I18nContextValue, I18nInstanceLike } from './types';
|
|
9
9
|
export { type UseI18nReturn, useI18n } from './useI18n';
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { type NativeI18nRewriteSnapshot } from '@modern-js/i18n-runtime-extensions/native';
|
|
2
|
+
import type { LocalisedUrlsOption } from '@modern-js/i18n-runtime-extensions/paths';
|
|
3
|
+
import type { NativeLocationRewrite } from '@modern-js/renderer-core/router';
|
|
4
|
+
import { type i18n as I18nInstance, type Resource } from 'i18next';
|
|
5
|
+
import type { I18nContextValue, I18nInstanceLike } from './i18n/types';
|
|
6
|
+
export type I18nSnapshotJson = null | boolean | number | string | I18nSnapshotJson[] | I18nSnapshotOptions;
|
|
7
|
+
export interface I18nSnapshotOptions {
|
|
8
|
+
[key: string]: I18nSnapshotJson;
|
|
9
|
+
}
|
|
10
|
+
export interface OctaneI18nProfileId {
|
|
11
|
+
key: string;
|
|
12
|
+
version: string;
|
|
13
|
+
}
|
|
14
|
+
export interface OctaneI18nSnapshot {
|
|
15
|
+
version: 1;
|
|
16
|
+
profile: OctaneI18nProfileId;
|
|
17
|
+
language: string;
|
|
18
|
+
languages: string[];
|
|
19
|
+
localisedUrls?: LocalisedUrlsOption;
|
|
20
|
+
options: I18nSnapshotOptions;
|
|
21
|
+
resources: Resource;
|
|
22
|
+
}
|
|
23
|
+
/** Named profiles install their custom translation functions locally. */
|
|
24
|
+
export interface OctaneI18nSnapshotProfile extends OctaneI18nProfileId {
|
|
25
|
+
captureOptions(instance: I18nInstanceLike): Readonly<I18nSnapshotOptions>;
|
|
26
|
+
validateOptions(options: Readonly<I18nSnapshotOptions>): void;
|
|
27
|
+
create(input: {
|
|
28
|
+
language: string;
|
|
29
|
+
options: Readonly<I18nSnapshotOptions>;
|
|
30
|
+
resources: Resource;
|
|
31
|
+
signal?: AbortSignal;
|
|
32
|
+
}): I18nInstance | Promise<I18nInstance>;
|
|
33
|
+
dispose?(instance: I18nInstance): void;
|
|
34
|
+
}
|
|
35
|
+
export declare function captureOctaneI18nRewriteSnapshot(rewrite: NativeLocationRewrite): NativeI18nRewriteSnapshot;
|
|
36
|
+
export declare function restoreOctaneI18nRewriteSnapshot(value: unknown): NativeLocationRewrite;
|
|
37
|
+
export declare function registerOctaneI18nSnapshotProfile(profile: OctaneI18nSnapshotProfile): () => void;
|
|
38
|
+
export declare function associateOctaneI18nSnapshotProfile(instance: I18nInstanceLike, id: OctaneI18nProfileId): void;
|
|
39
|
+
/** Built-in translation behavior only. Custom per-call processors need a profile. */
|
|
40
|
+
export declare function captureOctaneI18nSnapshot(value: I18nContextValue): OctaneI18nSnapshot;
|
|
41
|
+
export declare function restoreOctaneI18nSnapshot(value: unknown, signal?: AbortSignal): Promise<I18nContextValue>;
|
|
42
|
+
export declare function disposeOctaneI18nSnapshot(value: I18nContextValue): void;
|
|
43
|
+
export declare function registerOctaneI18nWorkerContexts(): () => void;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type AnyRouter, type RouterRenderSnapshot } from '@octanejs/tanstack-router';
|
|
2
|
+
export declare const OCTANE_FILE_ROUTE_SNAPSHOT_PROFILE: Readonly<{
|
|
3
|
+
key: "ultramodern.file-routes";
|
|
4
|
+
version: "1";
|
|
5
|
+
}>;
|
|
6
|
+
/** Associate the genuine router produced by the generated file-route factory. */
|
|
7
|
+
export declare function associateOctaneFileRouteSnapshot(router: AnyRouter): void;
|
|
8
|
+
export declare function captureOctaneRouterSnapshot(router: AnyRouter): RouterRenderSnapshot;
|
|
9
|
+
/** Startup registration uses the actual physical native router/match Contexts. */
|
|
10
|
+
export declare function registerOctaneRouterWorkerContexts(): () => void;
|
package/dist/types/server.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type DocumentAsset, type DocumentInlineData } from '@modern-js/renderer-core/document';
|
|
2
2
|
import type { NativeFederationBinding } from '@modern-js/renderer-core/federation';
|
|
3
3
|
import { type RequestSession, type ResponsePolicy } from '@modern-js/renderer-core/session';
|
|
4
|
+
import type { WorkerFederationTransport } from '@modern-js/renderer-core/worker-federation';
|
|
4
5
|
import type { AnyRouter } from '@octanejs/tanstack-router';
|
|
5
6
|
import { type ServerRenderNode, type StreamOptions } from 'octane/server';
|
|
6
7
|
export interface OctaneDocumentOptions {
|
|
@@ -32,6 +33,8 @@ export interface RenderOctaneApplicationOptions<Bindings extends object = object
|
|
|
32
33
|
readonly routerSerializationTimeoutMs?: number;
|
|
33
34
|
/** This server compilation's native Module Federation runtime. */
|
|
34
35
|
readonly federation?: NativeFederationBinding;
|
|
36
|
+
readonly workerFederation?: WorkerFederationTransport;
|
|
37
|
+
readonly workerFederationLocale?: string;
|
|
35
38
|
}
|
|
36
39
|
/** Deliver the CSR document; native browser mounting owns the empty root. */
|
|
37
40
|
export declare function renderOctaneCSRDocument<Bindings extends object>(input: OctaneDocumentResponseOptions<Bindings>): Promise<Response>;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { WorkerFederationTransport } from '@modern-js/renderer-core/worker-federation';
|
|
2
|
+
import { FederationApplication, type OctaneFederationScope } from './federation-context';
|
|
3
|
+
export interface OctaneWorkerFederationScope {
|
|
4
|
+
readonly transport: WorkerFederationTransport;
|
|
5
|
+
/** Compiled entry fallback; localized providers supply their live language. */
|
|
6
|
+
readonly locale: string;
|
|
7
|
+
/** Own document disposal; each lazy admission also owns its waiter deadline. */
|
|
8
|
+
readonly signal?: AbortSignal;
|
|
9
|
+
}
|
|
10
|
+
export type OctaneWorkerFederationFactory = (signal: AbortSignal) => OctaneWorkerFederationScope;
|
|
11
|
+
/** Each native document owns its transport and captured publisher authorities. */
|
|
12
|
+
export declare const WorkerFederationContext: import("octane").Context<OctaneWorkerFederationScope | null>;
|
|
13
|
+
export declare function WorkerFederationRoot(props: {
|
|
14
|
+
readonly scope: OctaneWorkerFederationScope;
|
|
15
|
+
readonly children?: unknown;
|
|
16
|
+
}): import("octane").ElementDescriptor<{
|
|
17
|
+
value: OctaneWorkerFederationScope;
|
|
18
|
+
children: unknown;
|
|
19
|
+
}>;
|
|
20
|
+
/** Stable native root used for startup and application updates alike. */
|
|
21
|
+
export declare function WorkerFederationApplication(props: {
|
|
22
|
+
readonly scope: OctaneWorkerFederationScope;
|
|
23
|
+
readonly federation?: OctaneFederationScope;
|
|
24
|
+
readonly application: Parameters<typeof FederationApplication>[0]['application'];
|
|
25
|
+
}): import("octane").ElementDescriptor<{
|
|
26
|
+
scope: OctaneWorkerFederationScope;
|
|
27
|
+
children: import("octane").ElementDescriptor<unknown> | import("octane").ElementDescriptor<{
|
|
28
|
+
scope: OctaneFederationScope;
|
|
29
|
+
application: {
|
|
30
|
+
readonly default: import("octane").ComponentBody;
|
|
31
|
+
readonly props?: unknown;
|
|
32
|
+
};
|
|
33
|
+
}>;
|
|
34
|
+
}>;
|