@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
package/src/server.ts
CHANGED
|
@@ -9,6 +9,9 @@ import {
|
|
|
9
9
|
type ResponsePolicy,
|
|
10
10
|
untilAborted,
|
|
11
11
|
} from '@modern-js/renderer-core/session';
|
|
12
|
+
import type { WorkerFederationTransport } from '@modern-js/renderer-core/worker-federation';
|
|
13
|
+
import type { AnyRouter } from '@octanejs/tanstack-router';
|
|
14
|
+
import { transformReadableStreamWithRouter } from '@octanejs/tanstack-router/ssr/server';
|
|
12
15
|
import {
|
|
13
16
|
createElement,
|
|
14
17
|
earlySignalBootstrapScript,
|
|
@@ -21,6 +24,7 @@ import {
|
|
|
21
24
|
assertOctaneIdentity,
|
|
22
25
|
} from './bootstrap';
|
|
23
26
|
import { createFederationScope, FederationRoot } from './federation-context';
|
|
27
|
+
import { WorkerFederationRoot } from './worker-federation-context';
|
|
24
28
|
|
|
25
29
|
export interface OctaneDocumentOptions {
|
|
26
30
|
/** One identity for this response, shared with the client's signal receiver. */
|
|
@@ -56,8 +60,12 @@ export interface RenderOctaneApplicationOptions<
|
|
|
56
60
|
readonly props?: unknown;
|
|
57
61
|
/** Native router serialization, owned by this request's router SSR context. */
|
|
58
62
|
readonly injection?: StreamOptions['injection'];
|
|
63
|
+
readonly router?: AnyRouter;
|
|
64
|
+
readonly routerSerializationTimeoutMs?: number;
|
|
59
65
|
/** This server compilation's native Module Federation runtime. */
|
|
60
66
|
readonly federation?: NativeFederationBinding;
|
|
67
|
+
readonly workerFederation?: WorkerFederationTransport;
|
|
68
|
+
readonly workerFederationLocale?: string;
|
|
61
69
|
}
|
|
62
70
|
|
|
63
71
|
function prepareOctaneDocument<Bindings extends object>(
|
|
@@ -166,9 +174,12 @@ export async function renderOctaneApplication<Bindings extends object>(
|
|
|
166
174
|
session.startRendering();
|
|
167
175
|
|
|
168
176
|
let head = '';
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
177
|
+
// Worker SSR and browser activation must own the same provider ranges,
|
|
178
|
+
// even when only the browser needs a Module Federation loader.
|
|
179
|
+
const federation =
|
|
180
|
+
input.federation || input.workerFederation
|
|
181
|
+
? createFederationScope(input.federation, true, document.nonce)
|
|
182
|
+
: undefined;
|
|
172
183
|
const App = federation
|
|
173
184
|
? () =>
|
|
174
185
|
createElement(FederationRoot, {
|
|
@@ -179,7 +190,24 @@ export async function renderOctaneApplication<Bindings extends object>(
|
|
|
179
190
|
: input.App,
|
|
180
191
|
})
|
|
181
192
|
: input.App;
|
|
182
|
-
|
|
193
|
+
// Native SSR retries the application while lazy boundaries settle. Keep
|
|
194
|
+
// their request-owned context identity stable across those render passes.
|
|
195
|
+
const workerFederation = input.workerFederation
|
|
196
|
+
? {
|
|
197
|
+
transport: input.workerFederation,
|
|
198
|
+
locale: input.workerFederationLocale ?? 'default',
|
|
199
|
+
signal: session.signal,
|
|
200
|
+
}
|
|
201
|
+
: undefined;
|
|
202
|
+
const NativeApp = workerFederation
|
|
203
|
+
? () =>
|
|
204
|
+
createElement(WorkerFederationRoot, {
|
|
205
|
+
scope: workerFederation,
|
|
206
|
+
children:
|
|
207
|
+
typeof App === 'function' ? createElement(App, input.props) : App,
|
|
208
|
+
})
|
|
209
|
+
: App;
|
|
210
|
+
const nativeStream = await renderToReadableStream(NativeApp, input.props, {
|
|
183
211
|
signal: session.signal,
|
|
184
212
|
...(document.nonce === undefined ? {} : { nonce: document.nonce }),
|
|
185
213
|
earlySignalBootstrap: 'external',
|
|
@@ -188,6 +216,7 @@ export async function renderOctaneApplication<Bindings extends object>(
|
|
|
188
216
|
buildId: document.nativeHydrationBuildId,
|
|
189
217
|
documentId: document.documentId,
|
|
190
218
|
},
|
|
219
|
+
externalSnapshots: { documentId: document.documentId },
|
|
191
220
|
headChannel: 'separate',
|
|
192
221
|
onHeadReady(value) {
|
|
193
222
|
head = value;
|
|
@@ -292,7 +321,18 @@ export async function renderOctaneApplication<Bindings extends object>(
|
|
|
292
321
|
},
|
|
293
322
|
{ highWaterMark: 0 },
|
|
294
323
|
);
|
|
295
|
-
return session.respond(
|
|
324
|
+
return session.respond(
|
|
325
|
+
input.router
|
|
326
|
+
? transformReadableStreamWithRouter(input.router, body, {
|
|
327
|
+
signal: session.signal,
|
|
328
|
+
rendererSafePoint: 'script-close',
|
|
329
|
+
timeoutMs: input.routerSerializationTimeoutMs,
|
|
330
|
+
onAbort: reason => {
|
|
331
|
+
void session.fail(reason);
|
|
332
|
+
},
|
|
333
|
+
})
|
|
334
|
+
: body,
|
|
335
|
+
);
|
|
296
336
|
} catch (error) {
|
|
297
337
|
await session.fail(error);
|
|
298
338
|
throw error;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { WorkerFederationTransport } from '@modern-js/renderer-core/worker-federation';
|
|
2
|
+
import { createContext, createElement } from 'octane';
|
|
3
|
+
import {
|
|
4
|
+
FederationApplication,
|
|
5
|
+
type OctaneFederationScope,
|
|
6
|
+
} from './federation-context';
|
|
7
|
+
|
|
8
|
+
export interface OctaneWorkerFederationScope {
|
|
9
|
+
readonly transport: WorkerFederationTransport;
|
|
10
|
+
/** Compiled entry fallback; localized providers supply their live language. */
|
|
11
|
+
readonly locale: string;
|
|
12
|
+
/** Own document disposal; each lazy admission also owns its waiter deadline. */
|
|
13
|
+
readonly signal?: AbortSignal;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export type OctaneWorkerFederationFactory = (
|
|
17
|
+
signal: AbortSignal,
|
|
18
|
+
) => OctaneWorkerFederationScope;
|
|
19
|
+
|
|
20
|
+
/** Each native document owns its transport and captured publisher authorities. */
|
|
21
|
+
export const WorkerFederationContext =
|
|
22
|
+
createContext<OctaneWorkerFederationScope | null>(null);
|
|
23
|
+
|
|
24
|
+
export function WorkerFederationRoot(props: {
|
|
25
|
+
readonly scope: OctaneWorkerFederationScope;
|
|
26
|
+
readonly children?: unknown;
|
|
27
|
+
}) {
|
|
28
|
+
return createElement(WorkerFederationContext, {
|
|
29
|
+
value: props.scope,
|
|
30
|
+
children: props.children,
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Stable native root used for startup and application updates alike. */
|
|
35
|
+
export function WorkerFederationApplication(props: {
|
|
36
|
+
readonly scope: OctaneWorkerFederationScope;
|
|
37
|
+
readonly federation?: OctaneFederationScope;
|
|
38
|
+
readonly application: Parameters<
|
|
39
|
+
typeof FederationApplication
|
|
40
|
+
>[0]['application'];
|
|
41
|
+
}) {
|
|
42
|
+
return createElement(WorkerFederationRoot, {
|
|
43
|
+
scope: props.scope,
|
|
44
|
+
children: props.federation
|
|
45
|
+
? createElement(FederationApplication, {
|
|
46
|
+
scope: props.federation,
|
|
47
|
+
application: props.application,
|
|
48
|
+
})
|
|
49
|
+
: createElement(props.application.default, props.application.props),
|
|
50
|
+
});
|
|
51
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import type { NativeLocationRewrite } from '@modern-js/renderer-core/router';
|
|
2
|
+
import {
|
|
3
|
+
type Context,
|
|
4
|
+
type ExternalSnapshotContextCodec,
|
|
5
|
+
registerExternalSnapshotContext,
|
|
6
|
+
useContext,
|
|
7
|
+
} from 'octane';
|
|
8
|
+
|
|
9
|
+
export const OCTANE_WORKER_CONTEXT_KEYS = Object.freeze({
|
|
10
|
+
router: 'ultramodern.octane.router-context.v1',
|
|
11
|
+
match: 'ultramodern.octane.match-context.v1',
|
|
12
|
+
i18n: 'ultramodern.octane.i18n-context.v1',
|
|
13
|
+
} as const);
|
|
14
|
+
|
|
15
|
+
type OctaneWorkerContextKey =
|
|
16
|
+
(typeof OCTANE_WORKER_CONTEXT_KEYS)[keyof typeof OCTANE_WORKER_CONTEXT_KEYS];
|
|
17
|
+
|
|
18
|
+
export interface OctaneWorkerContextCodec<T>
|
|
19
|
+
extends ExternalSnapshotContextCodec<T> {
|
|
20
|
+
readonly key: OctaneWorkerContextKey;
|
|
21
|
+
readonly dispose?: (value: T) => void | PromiseLike<void>;
|
|
22
|
+
readonly isProvided: (value: T) => boolean;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const builtinKeys = Object.values(OCTANE_WORKER_CONTEXT_KEYS);
|
|
26
|
+
const contexts = new Map<OctaneWorkerContextKey, () => boolean>();
|
|
27
|
+
|
|
28
|
+
/** Optional renderer modules register their actual native context identities. */
|
|
29
|
+
export function registerOctaneWorkerContext<T>(
|
|
30
|
+
context: Context<T>,
|
|
31
|
+
codec: OctaneWorkerContextCodec<T>,
|
|
32
|
+
): () => void {
|
|
33
|
+
const { key, isProvided } = codec;
|
|
34
|
+
if (!builtinKeys.includes(key) || typeof isProvided !== 'function')
|
|
35
|
+
throw new TypeError('Invalid Octane Worker builtin context codec');
|
|
36
|
+
|
|
37
|
+
const unregister = registerExternalSnapshotContext(context, codec);
|
|
38
|
+
const hasProvider = () => isProvided(useContext(context));
|
|
39
|
+
contexts.set(key, hasProvider);
|
|
40
|
+
|
|
41
|
+
return () => {
|
|
42
|
+
if (contexts.get(key) !== hasProvider) return;
|
|
43
|
+
unregister();
|
|
44
|
+
contexts.delete(key);
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Read live providers on every render; explicit requirements retain native validation. */
|
|
49
|
+
export function useOctaneWorkerContextKeys(
|
|
50
|
+
explicit: readonly string[] = [],
|
|
51
|
+
): readonly string[] {
|
|
52
|
+
const selected: string[] = [];
|
|
53
|
+
for (const key of builtinKeys) {
|
|
54
|
+
const hasProvider = contexts.get(key);
|
|
55
|
+
if (hasProvider?.() && !explicit.includes(key)) selected.push(key);
|
|
56
|
+
}
|
|
57
|
+
return [...selected, ...explicit];
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export interface OctaneWorkerRouterRewrite {
|
|
61
|
+
readonly capture: (rewrite: NativeLocationRewrite) => unknown;
|
|
62
|
+
readonly restore: (descriptor: unknown) => NativeLocationRewrite;
|
|
63
|
+
}
|
|
64
|
+
let routerRewrite: OctaneWorkerRouterRewrite | undefined;
|
|
65
|
+
|
|
66
|
+
/** The optional i18n owner supplies its own native rewrite provenance. */
|
|
67
|
+
export function registerOctaneWorkerRouterRewrite(
|
|
68
|
+
codec: OctaneWorkerRouterRewrite,
|
|
69
|
+
): () => void {
|
|
70
|
+
if (
|
|
71
|
+
routerRewrite ||
|
|
72
|
+
typeof codec.capture !== 'function' ||
|
|
73
|
+
typeof codec.restore !== 'function'
|
|
74
|
+
)
|
|
75
|
+
throw new TypeError(
|
|
76
|
+
'Invalid or duplicate Octane Worker router rewrite codec',
|
|
77
|
+
);
|
|
78
|
+
const captured = Object.freeze({
|
|
79
|
+
capture: codec.capture,
|
|
80
|
+
restore: codec.restore,
|
|
81
|
+
});
|
|
82
|
+
routerRewrite = captured;
|
|
83
|
+
return () => {
|
|
84
|
+
if (routerRewrite === captured) routerRewrite = undefined;
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
export function getOctaneWorkerRouterRewrite():
|
|
88
|
+
| OctaneWorkerRouterRewrite
|
|
89
|
+
| undefined {
|
|
90
|
+
return routerRewrite;
|
|
91
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import type { NativeWorkerDispatchOptions } from '@modern-js/renderer-core/server';
|
|
2
|
+
import {
|
|
3
|
+
type AdmitWorkerRendererContract,
|
|
4
|
+
createWorkerFederationHandler,
|
|
5
|
+
createWorkerFederationTransport,
|
|
6
|
+
} from '@modern-js/renderer-core/worker-federation';
|
|
7
|
+
import {
|
|
8
|
+
type ExternalSnapshotRenderOptions,
|
|
9
|
+
prepareExternalSnapshotRequest,
|
|
10
|
+
releasePreparedExternalSnapshotRequest,
|
|
11
|
+
renderExternalSnapshot,
|
|
12
|
+
type ServerRenderNode,
|
|
13
|
+
serializeExternalSnapshot,
|
|
14
|
+
} from 'octane/server';
|
|
15
|
+
import { createFederationScope, FederationContext } from './federation-context';
|
|
16
|
+
import { WorkerFederationContext } from './worker-federation-context';
|
|
17
|
+
|
|
18
|
+
type NativeSnapshotRequest = Awaited<
|
|
19
|
+
ReturnType<typeof prepareExternalSnapshotRequest>
|
|
20
|
+
>;
|
|
21
|
+
type HandlerOptions = Parameters<
|
|
22
|
+
typeof createWorkerFederationHandler<NativeSnapshotRequest>
|
|
23
|
+
>[0];
|
|
24
|
+
|
|
25
|
+
function isNativeWorkerComponent(
|
|
26
|
+
value: unknown,
|
|
27
|
+
): value is Extract<ServerRenderNode, (props: never) => unknown> {
|
|
28
|
+
return typeof value === 'function';
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** The compiled registry pins its renderer publication and literal imports. */
|
|
32
|
+
export interface OctaneWorkerFederationRegistry {
|
|
33
|
+
initialize(
|
|
34
|
+
options: Omit<HandlerOptions, 'publication' | 'exposes'> & {
|
|
35
|
+
publication: unknown;
|
|
36
|
+
},
|
|
37
|
+
): (request: Request) => Promise<Response | undefined>;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Created per dispatch; no publisher or native snapshot cache crosses documents. */
|
|
41
|
+
export function createNativeWorkerFederationRuntime<Bindings extends object>(
|
|
42
|
+
options: NativeWorkerDispatchOptions<Bindings>,
|
|
43
|
+
compiler: {
|
|
44
|
+
readonly admitRendererContract: AdmitWorkerRendererContract;
|
|
45
|
+
readonly registry?: OctaneWorkerFederationRegistry;
|
|
46
|
+
},
|
|
47
|
+
) {
|
|
48
|
+
const { resources } = options;
|
|
49
|
+
const bindings = options.bindings;
|
|
50
|
+
const transport = resources.workerFederationServices?.length
|
|
51
|
+
? createWorkerFederationTransport({
|
|
52
|
+
admitRendererContract: compiler.admitRendererContract,
|
|
53
|
+
services: resources.workerFederationServices.map(service => {
|
|
54
|
+
const binding: unknown =
|
|
55
|
+
bindings && Reflect.get(bindings, service.binding);
|
|
56
|
+
if (
|
|
57
|
+
!binding ||
|
|
58
|
+
(typeof binding !== 'object' && typeof binding !== 'function') ||
|
|
59
|
+
!('fetch' in binding) ||
|
|
60
|
+
typeof binding.fetch !== 'function'
|
|
61
|
+
)
|
|
62
|
+
throw new Error(
|
|
63
|
+
`Native Worker federation requires service binding ${service.binding}.`,
|
|
64
|
+
);
|
|
65
|
+
return { ...service, fetch: binding.fetch.bind(binding) };
|
|
66
|
+
}),
|
|
67
|
+
})
|
|
68
|
+
: undefined;
|
|
69
|
+
let handle: ((request: Request) => Promise<Response | undefined>) | undefined;
|
|
70
|
+
if (resources.workerFederation) {
|
|
71
|
+
if (!compiler.registry || !resources.workerFederationManifestUrl)
|
|
72
|
+
throw new Error(
|
|
73
|
+
'Native Worker federation requires its compiled expose registry and pinned manifest URL.',
|
|
74
|
+
);
|
|
75
|
+
handle = compiler.registry.initialize({
|
|
76
|
+
publication: resources.workerFederation.worker,
|
|
77
|
+
manifestUrl: resources.workerFederationManifestUrl,
|
|
78
|
+
admitRendererContract: compiler.admitRendererContract,
|
|
79
|
+
admitNativeRequest(payload, { admitted, signal }) {
|
|
80
|
+
const initializeContexts: ExternalSnapshotRenderOptions['initializeContexts'] =
|
|
81
|
+
transport
|
|
82
|
+
? (provide, request) => {
|
|
83
|
+
provide(
|
|
84
|
+
FederationContext,
|
|
85
|
+
createFederationScope(undefined, true, request.nonce),
|
|
86
|
+
);
|
|
87
|
+
provide(WorkerFederationContext, {
|
|
88
|
+
transport,
|
|
89
|
+
locale: admitted.locale,
|
|
90
|
+
signal,
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
: undefined;
|
|
94
|
+
return prepareExternalSnapshotRequest(payload, {
|
|
95
|
+
authority: admitted.coordinate.native,
|
|
96
|
+
signal,
|
|
97
|
+
...(initializeContexts ? { initializeContexts } : {}),
|
|
98
|
+
});
|
|
99
|
+
},
|
|
100
|
+
releaseNativeRequest: releasePreparedExternalSnapshotRequest,
|
|
101
|
+
async renderNativeSnapshot(module, request, { admitted }) {
|
|
102
|
+
const component =
|
|
103
|
+
module && typeof module === 'object' && 'default' in module
|
|
104
|
+
? module.default
|
|
105
|
+
: undefined;
|
|
106
|
+
if (!isNativeWorkerComponent(component))
|
|
107
|
+
throw new TypeError(
|
|
108
|
+
`Native Worker expose ${admitted.expose} requires a default Octane component.`,
|
|
109
|
+
);
|
|
110
|
+
return serializeExternalSnapshot(
|
|
111
|
+
await renderExternalSnapshot(component, request),
|
|
112
|
+
);
|
|
113
|
+
},
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
return {
|
|
117
|
+
transport,
|
|
118
|
+
handleRequest: handle ?? (() => Promise.resolve(undefined)),
|
|
119
|
+
};
|
|
120
|
+
}
|
|
@@ -1,139 +0,0 @@
|
|
|
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
|
-
createOctaneRouterInjection: ()=>createOctaneRouterInjection
|
|
32
|
-
});
|
|
33
|
-
const external_router_server_cleanup_js_namespaceObject = require("./router-server-cleanup.js");
|
|
34
|
-
const SERIALIZATION_TIMEOUT_MS = 60000;
|
|
35
|
-
function createOctaneRouterInjection(router, session, options = {}) {
|
|
36
|
-
const serverSsr = router.serverSsr;
|
|
37
|
-
if (!serverSsr) throw new Error('Native Octane router.serverSsr is required for streaming');
|
|
38
|
-
const timeoutMs = options.serializationTimeoutMs ?? SERIALIZATION_TIMEOUT_MS;
|
|
39
|
-
if (!Number.isInteger(timeoutMs) || timeoutMs <= 0 || timeoutMs > SERIALIZATION_TIMEOUT_MS) throw new TypeError('Octane router serialization timeout must be 1 to 60000 ms');
|
|
40
|
-
session.signal.throwIfAborted();
|
|
41
|
-
let settled = false;
|
|
42
|
-
let released = false;
|
|
43
|
-
let renderFinished = false;
|
|
44
|
-
let timer;
|
|
45
|
-
let stopSerialization;
|
|
46
|
-
const subscriptions = new Set();
|
|
47
|
-
let resolveDone;
|
|
48
|
-
let rejectDone;
|
|
49
|
-
const done = new Promise((resolve, reject)=>{
|
|
50
|
-
resolveDone = resolve;
|
|
51
|
-
rejectDone = reject;
|
|
52
|
-
});
|
|
53
|
-
done.catch(()=>{});
|
|
54
|
-
const settle = (error)=>{
|
|
55
|
-
if (settled) return;
|
|
56
|
-
settled = true;
|
|
57
|
-
if (void 0 !== timer) clearTimeout(timer);
|
|
58
|
-
timer = void 0;
|
|
59
|
-
stopSerialization?.();
|
|
60
|
-
stopSerialization = void 0;
|
|
61
|
-
if (void 0 === error) resolveDone();
|
|
62
|
-
else rejectDone(error);
|
|
63
|
-
};
|
|
64
|
-
const release = ()=>{
|
|
65
|
-
if (released) return;
|
|
66
|
-
released = true;
|
|
67
|
-
if (void 0 !== timer) clearTimeout(timer);
|
|
68
|
-
timer = void 0;
|
|
69
|
-
stopSerialization?.();
|
|
70
|
-
stopSerialization = void 0;
|
|
71
|
-
for (const unsubscribe of subscriptions)unsubscribe();
|
|
72
|
-
subscriptions.clear();
|
|
73
|
-
session.signal.removeEventListener('abort', onAbort);
|
|
74
|
-
if (!settled) settle(session.signal.reason ?? new Error('Octane router serialization was interrupted'));
|
|
75
|
-
};
|
|
76
|
-
function onAbort() {
|
|
77
|
-
settle(session.signal.reason ?? new DOMException('Octane request aborted', 'AbortError'));
|
|
78
|
-
release();
|
|
79
|
-
}
|
|
80
|
-
if (serverSsr.isSerializationFinished()) settle();
|
|
81
|
-
else stopSerialization = serverSsr.onSerializationFinished(()=>settle());
|
|
82
|
-
serverSsr.onCleanup(release);
|
|
83
|
-
session.signal.addEventListener('abort', onAbort, {
|
|
84
|
-
once: true
|
|
85
|
-
});
|
|
86
|
-
try {
|
|
87
|
-
session.registerCleanup(()=>{
|
|
88
|
-
release();
|
|
89
|
-
(0, external_router_server_cleanup_js_namespaceObject.cleanupOctaneRouterSSR)(router);
|
|
90
|
-
});
|
|
91
|
-
} catch (error) {
|
|
92
|
-
release();
|
|
93
|
-
(0, external_router_server_cleanup_js_namespaceObject.cleanupOctaneRouterSSR)(router);
|
|
94
|
-
throw error;
|
|
95
|
-
}
|
|
96
|
-
return {
|
|
97
|
-
take () {
|
|
98
|
-
if (released) return '';
|
|
99
|
-
serverSsr.liftScriptBarrier();
|
|
100
|
-
return serverSsr.takeBufferedHtml() ?? '';
|
|
101
|
-
},
|
|
102
|
-
subscribe (notify) {
|
|
103
|
-
if (released) return ()=>{};
|
|
104
|
-
const stop = serverSsr.onInjectedHtml(notify);
|
|
105
|
-
let subscribed = true;
|
|
106
|
-
const unsubscribe = ()=>{
|
|
107
|
-
if (!subscribed) return;
|
|
108
|
-
subscribed = false;
|
|
109
|
-
subscriptions.delete(unsubscribe);
|
|
110
|
-
stop();
|
|
111
|
-
};
|
|
112
|
-
subscriptions.add(unsubscribe);
|
|
113
|
-
return unsubscribe;
|
|
114
|
-
},
|
|
115
|
-
done,
|
|
116
|
-
cancel (reason) {
|
|
117
|
-
settle(reason ?? new DOMException('Octane rendering cancelled', 'AbortError'));
|
|
118
|
-
release();
|
|
119
|
-
session.abort(reason);
|
|
120
|
-
},
|
|
121
|
-
renderComplete () {
|
|
122
|
-
if (released || renderFinished) return;
|
|
123
|
-
renderFinished = true;
|
|
124
|
-
serverSsr.setRenderFinished();
|
|
125
|
-
if (!serverSsr.isSerializationFinished() && !settled) timer = setTimeout(()=>{
|
|
126
|
-
const error = new Error('Octane router serialization timed out after app rendering');
|
|
127
|
-
settle(error);
|
|
128
|
-
session.fail(error);
|
|
129
|
-
}, timeoutMs);
|
|
130
|
-
}
|
|
131
|
-
};
|
|
132
|
-
}
|
|
133
|
-
exports.createOctaneRouterInjection = __webpack_exports__.createOctaneRouterInjection;
|
|
134
|
-
for(var __rspack_i in __webpack_exports__)if (-1 === [
|
|
135
|
-
"createOctaneRouterInjection"
|
|
136
|
-
].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
|
|
137
|
-
Object.defineProperty(exports, '__esModule', {
|
|
138
|
-
value: true
|
|
139
|
-
});
|
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
import { cleanupOctaneRouterSSR } from "./router-server-cleanup.mjs";
|
|
2
|
-
const SERIALIZATION_TIMEOUT_MS = 60000;
|
|
3
|
-
function createOctaneRouterInjection(router, session, options = {}) {
|
|
4
|
-
const serverSsr = router.serverSsr;
|
|
5
|
-
if (!serverSsr) throw new Error('Native Octane router.serverSsr is required for streaming');
|
|
6
|
-
const timeoutMs = options.serializationTimeoutMs ?? SERIALIZATION_TIMEOUT_MS;
|
|
7
|
-
if (!Number.isInteger(timeoutMs) || timeoutMs <= 0 || timeoutMs > SERIALIZATION_TIMEOUT_MS) throw new TypeError('Octane router serialization timeout must be 1 to 60000 ms');
|
|
8
|
-
session.signal.throwIfAborted();
|
|
9
|
-
let settled = false;
|
|
10
|
-
let released = false;
|
|
11
|
-
let renderFinished = false;
|
|
12
|
-
let timer;
|
|
13
|
-
let stopSerialization;
|
|
14
|
-
const subscriptions = new Set();
|
|
15
|
-
let resolveDone;
|
|
16
|
-
let rejectDone;
|
|
17
|
-
const done = new Promise((resolve, reject)=>{
|
|
18
|
-
resolveDone = resolve;
|
|
19
|
-
rejectDone = reject;
|
|
20
|
-
});
|
|
21
|
-
done.catch(()=>{});
|
|
22
|
-
const settle = (error)=>{
|
|
23
|
-
if (settled) return;
|
|
24
|
-
settled = true;
|
|
25
|
-
if (void 0 !== timer) clearTimeout(timer);
|
|
26
|
-
timer = void 0;
|
|
27
|
-
stopSerialization?.();
|
|
28
|
-
stopSerialization = void 0;
|
|
29
|
-
if (void 0 === error) resolveDone();
|
|
30
|
-
else rejectDone(error);
|
|
31
|
-
};
|
|
32
|
-
const release = ()=>{
|
|
33
|
-
if (released) return;
|
|
34
|
-
released = true;
|
|
35
|
-
if (void 0 !== timer) clearTimeout(timer);
|
|
36
|
-
timer = void 0;
|
|
37
|
-
stopSerialization?.();
|
|
38
|
-
stopSerialization = void 0;
|
|
39
|
-
for (const unsubscribe of subscriptions)unsubscribe();
|
|
40
|
-
subscriptions.clear();
|
|
41
|
-
session.signal.removeEventListener('abort', onAbort);
|
|
42
|
-
if (!settled) settle(session.signal.reason ?? new Error('Octane router serialization was interrupted'));
|
|
43
|
-
};
|
|
44
|
-
function onAbort() {
|
|
45
|
-
settle(session.signal.reason ?? new DOMException('Octane request aborted', 'AbortError'));
|
|
46
|
-
release();
|
|
47
|
-
}
|
|
48
|
-
if (serverSsr.isSerializationFinished()) settle();
|
|
49
|
-
else stopSerialization = serverSsr.onSerializationFinished(()=>settle());
|
|
50
|
-
serverSsr.onCleanup(release);
|
|
51
|
-
session.signal.addEventListener('abort', onAbort, {
|
|
52
|
-
once: true
|
|
53
|
-
});
|
|
54
|
-
try {
|
|
55
|
-
session.registerCleanup(()=>{
|
|
56
|
-
release();
|
|
57
|
-
cleanupOctaneRouterSSR(router);
|
|
58
|
-
});
|
|
59
|
-
} catch (error) {
|
|
60
|
-
release();
|
|
61
|
-
cleanupOctaneRouterSSR(router);
|
|
62
|
-
throw error;
|
|
63
|
-
}
|
|
64
|
-
return {
|
|
65
|
-
take () {
|
|
66
|
-
if (released) return '';
|
|
67
|
-
serverSsr.liftScriptBarrier();
|
|
68
|
-
return serverSsr.takeBufferedHtml() ?? '';
|
|
69
|
-
},
|
|
70
|
-
subscribe (notify) {
|
|
71
|
-
if (released) return ()=>{};
|
|
72
|
-
const stop = serverSsr.onInjectedHtml(notify);
|
|
73
|
-
let subscribed = true;
|
|
74
|
-
const unsubscribe = ()=>{
|
|
75
|
-
if (!subscribed) return;
|
|
76
|
-
subscribed = false;
|
|
77
|
-
subscriptions.delete(unsubscribe);
|
|
78
|
-
stop();
|
|
79
|
-
};
|
|
80
|
-
subscriptions.add(unsubscribe);
|
|
81
|
-
return unsubscribe;
|
|
82
|
-
},
|
|
83
|
-
done,
|
|
84
|
-
cancel (reason) {
|
|
85
|
-
settle(reason ?? new DOMException('Octane rendering cancelled', 'AbortError'));
|
|
86
|
-
release();
|
|
87
|
-
session.abort(reason);
|
|
88
|
-
},
|
|
89
|
-
renderComplete () {
|
|
90
|
-
if (released || renderFinished) return;
|
|
91
|
-
renderFinished = true;
|
|
92
|
-
serverSsr.setRenderFinished();
|
|
93
|
-
if (!serverSsr.isSerializationFinished() && !settled) timer = setTimeout(()=>{
|
|
94
|
-
const error = new Error('Octane router serialization timed out after app rendering');
|
|
95
|
-
settle(error);
|
|
96
|
-
session.fail(error);
|
|
97
|
-
}, timeoutMs);
|
|
98
|
-
}
|
|
99
|
-
};
|
|
100
|
-
}
|
|
101
|
-
export { createOctaneRouterInjection };
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import type { RequestSession } from '@modern-js/renderer-core/session';
|
|
2
|
-
import type { AnyRouter } from '@octanejs/tanstack-router';
|
|
3
|
-
import type { StreamInjectionSource } from 'octane/server';
|
|
4
|
-
export interface OctaneRouterInjectionOptions {
|
|
5
|
-
serializationTimeoutMs?: number;
|
|
6
|
-
}
|
|
7
|
-
/** Couple native router serialization to the response's single stream owner. */
|
|
8
|
-
export declare function createOctaneRouterInjection(router: AnyRouter, session: RequestSession, options?: OctaneRouterInjectionOptions): StreamInjectionSource;
|