@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.
Files changed (96) hide show
  1. package/dist/cjs/client.js +56 -6
  2. package/dist/cjs/entry-application.js +7 -2
  3. package/dist/cjs/entry-client.js +3 -0
  4. package/dist/cjs/entry-server.js +13 -5
  5. package/dist/cjs/federation.js +108 -6
  6. package/dist/cjs/i18n/I18nProvider.js +6 -14
  7. package/dist/cjs/i18n/LocalizedLink.js +24 -33
  8. package/dist/cjs/i18n/index.js +0 -3
  9. package/dist/cjs/i18n-external-snapshot.js +645 -0
  10. package/dist/cjs/index.js +4 -4
  11. package/dist/cjs/manifest.js +2 -2
  12. package/dist/cjs/plugin/compiler.js +2 -5
  13. package/dist/cjs/plugin/index.js +2 -2
  14. package/dist/cjs/plugin/profile.js +12 -10
  15. package/dist/cjs/router-client.js +1 -1
  16. package/dist/cjs/router-external-snapshot.js +263 -0
  17. package/dist/cjs/router-handler.js +11 -8
  18. package/dist/cjs/router-server.js +3 -11
  19. package/dist/cjs/router.js +1 -1
  20. package/dist/cjs/routes.js +11 -17
  21. package/dist/cjs/server.js +28 -7
  22. package/dist/cjs/worker-federation-context.js +63 -0
  23. package/dist/cjs/worker-federation-contexts.js +97 -0
  24. package/dist/cjs/worker-federation.js +97 -0
  25. package/dist/esm/client.mjs +56 -6
  26. package/dist/esm/entry-application.mjs +7 -2
  27. package/dist/esm/entry-client.mjs +3 -0
  28. package/dist/esm/entry-server.mjs +11 -6
  29. package/dist/esm/federation.mjs +110 -8
  30. package/dist/esm/i18n/I18nProvider.mjs +7 -15
  31. package/dist/esm/i18n/LocalizedLink.mjs +24 -30
  32. package/dist/esm/i18n/index.mjs +1 -1
  33. package/dist/esm/i18n-external-snapshot.mjs +576 -0
  34. package/dist/esm/index.mjs +4 -4
  35. package/dist/esm/manifest.mjs +2 -2
  36. package/dist/esm/router-client.mjs +1 -1
  37. package/dist/esm/router-external-snapshot.mjs +216 -0
  38. package/dist/esm/router-handler.mjs +11 -8
  39. package/dist/esm/router-server.mjs +0 -1
  40. package/dist/esm/router.mjs +2 -2
  41. package/dist/esm/routes.mjs +11 -17
  42. package/dist/esm/server.mjs +24 -3
  43. package/dist/esm/worker-federation-context.mjs +19 -0
  44. package/dist/esm/worker-federation-contexts.mjs +47 -0
  45. package/dist/esm/worker-federation.mjs +59 -0
  46. package/dist/esm-node/manifest.mjs +2 -2
  47. package/dist/esm-node/plugin/compiler.mjs +2 -5
  48. package/dist/esm-node/plugin/index.mjs +2 -2
  49. package/dist/esm-node/plugin/profile.mjs +12 -10
  50. package/dist/types/client.d.ts +5 -1
  51. package/dist/types/entry-client.d.ts +2 -0
  52. package/dist/types/entry-server.d.ts +4 -1
  53. package/dist/types/federation.d.ts +2 -0
  54. package/dist/types/i18n/LocalizedLink.d.ts +5 -32
  55. package/dist/types/i18n/context.d.ts +1 -1
  56. package/dist/types/i18n/index.d.ts +1 -1
  57. package/dist/types/i18n-external-snapshot.d.ts +43 -0
  58. package/dist/types/index.d.ts +4 -4
  59. package/dist/types/manifest.d.ts +2 -2
  60. package/dist/types/router-external-snapshot.d.ts +10 -0
  61. package/dist/types/router-server.d.ts +0 -2
  62. package/dist/types/server.d.ts +6 -0
  63. package/dist/types/worker-federation-context.d.ts +34 -0
  64. package/dist/types/worker-federation-contexts.d.ts +25 -0
  65. package/dist/types/worker-federation.d.ts +20 -0
  66. package/package.json +84 -22
  67. package/src/client.ts +99 -16
  68. package/src/entry-application.ts +7 -3
  69. package/src/entry-client.ts +10 -0
  70. package/src/entry-server.ts +20 -9
  71. package/src/federation.ts +159 -15
  72. package/src/i18n/I18nProvider.ts +11 -20
  73. package/src/i18n/LocalizedLink.ts +46 -72
  74. package/src/i18n/context.ts +1 -1
  75. package/src/i18n/index.ts +1 -5
  76. package/src/i18n-external-snapshot.ts +803 -0
  77. package/src/index.ts +4 -4
  78. package/src/manifest.ts +2 -2
  79. package/src/plugin/compiler-manifest.cjs +27 -1
  80. package/src/plugin/compiler.ts +6 -14
  81. package/src/plugin/index.ts +4 -2
  82. package/src/plugin/profile.ts +14 -10
  83. package/src/router-client.ts +1 -1
  84. package/src/router-external-snapshot.ts +306 -0
  85. package/src/router-handler.ts +11 -8
  86. package/src/router-server.ts +0 -2
  87. package/src/router.ts +2 -2
  88. package/src/routes.ts +28 -26
  89. package/src/server.ts +45 -5
  90. package/src/worker-federation-context.ts +51 -0
  91. package/src/worker-federation-contexts.ts +91 -0
  92. package/src/worker-federation.ts +120 -0
  93. package/dist/cjs/router-injection.js +0 -139
  94. package/dist/esm/router-injection.mjs +0 -101
  95. package/dist/types/router-injection.d.ts +0 -8
  96. 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
- const federation = input.federation
170
- ? createFederationScope(input.federation, true, document.nonce)
171
- : undefined;
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
- const nativeStream = await renderToReadableStream(App, input.props, {
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(body);
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;