@bleedingdev/modern-js-renderer-octane 0.0.0-stage → 3.9.3-ultramodern.10

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 (154) hide show
  1. package/LICENSE +21 -0
  2. package/bin/octane-tsc.mjs +4 -0
  3. package/dist/cjs/bootstrap.js +68 -0
  4. package/dist/cjs/client.js +421 -0
  5. package/dist/cjs/entry-application.js +88 -0
  6. package/dist/cjs/entry-client.js +95 -0
  7. package/dist/cjs/entry-server.js +135 -0
  8. package/dist/cjs/federation-context.js +72 -0
  9. package/dist/cjs/federation.js +205 -0
  10. package/dist/cjs/i18n/I18nProvider.js +59 -0
  11. package/dist/cjs/i18n/LocalizedLink.js +80 -0
  12. package/dist/cjs/i18n/context.js +41 -0
  13. package/dist/cjs/i18n/index.js +83 -0
  14. package/dist/cjs/i18n/languageSync.js +99 -0
  15. package/dist/cjs/i18n/types.js +18 -0
  16. package/dist/cjs/i18n/useI18n.js +110 -0
  17. package/dist/cjs/i18n-external-snapshot.js +645 -0
  18. package/dist/cjs/index.js +46 -0
  19. package/dist/cjs/manifest.js +167 -0
  20. package/dist/cjs/plugin/compiler.js +122 -0
  21. package/dist/cjs/plugin/create.js +266 -0
  22. package/dist/cjs/plugin/index.js +151 -0
  23. package/dist/cjs/plugin/profile.js +91 -0
  24. package/dist/cjs/plugin/source.js +56 -0
  25. package/dist/cjs/router-client.js +139 -0
  26. package/dist/cjs/router-external-snapshot.js +291 -0
  27. package/dist/cjs/router-handler.js +171 -0
  28. package/dist/cjs/router-server-cleanup.js +46 -0
  29. package/dist/cjs/router-server-snapshot.js +405 -0
  30. package/dist/cjs/router-server.js +100 -0
  31. package/dist/cjs/router-snapshot.js +270 -0
  32. package/dist/cjs/router.js +218 -0
  33. package/dist/cjs/routes.js +190 -0
  34. package/dist/cjs/server.js +245 -0
  35. package/dist/cjs/typecheck.js +615 -0
  36. package/dist/cjs/worker-federation-context.js +63 -0
  37. package/dist/cjs/worker-federation-contexts.js +97 -0
  38. package/dist/cjs/worker-federation.js +97 -0
  39. package/dist/esm/bootstrap.mjs +24 -0
  40. package/dist/esm/client.mjs +377 -0
  41. package/dist/esm/entry-application.mjs +44 -0
  42. package/dist/esm/entry-client.mjs +57 -0
  43. package/dist/esm/entry-server.mjs +91 -0
  44. package/dist/esm/federation-context.mjs +25 -0
  45. package/dist/esm/federation.mjs +167 -0
  46. package/dist/esm/i18n/I18nProvider.mjs +21 -0
  47. package/dist/esm/i18n/LocalizedLink.mjs +42 -0
  48. package/dist/esm/i18n/context.mjs +3 -0
  49. package/dist/esm/i18n/index.mjs +8 -0
  50. package/dist/esm/i18n/languageSync.mjs +61 -0
  51. package/dist/esm/i18n/types.mjs +0 -0
  52. package/dist/esm/i18n/useI18n.mjs +72 -0
  53. package/dist/esm/i18n-external-snapshot.mjs +576 -0
  54. package/dist/esm/index.mjs +8 -0
  55. package/dist/esm/manifest.mjs +117 -0
  56. package/dist/esm/router-client.mjs +98 -0
  57. package/dist/esm/router-external-snapshot.mjs +244 -0
  58. package/dist/esm/router-handler.mjs +133 -0
  59. package/dist/esm/router-server-cleanup.mjs +8 -0
  60. package/dist/esm/router-server-snapshot.mjs +367 -0
  61. package/dist/esm/router-server.mjs +11 -0
  62. package/dist/esm/router-snapshot.mjs +199 -0
  63. package/dist/esm/router.mjs +94 -0
  64. package/dist/esm/routes.mjs +134 -0
  65. package/dist/esm/server.mjs +204 -0
  66. package/dist/esm/worker-federation-context.mjs +19 -0
  67. package/dist/esm/worker-federation-contexts.mjs +47 -0
  68. package/dist/esm/worker-federation.mjs +59 -0
  69. package/dist/esm-node/bootstrap.mjs +24 -0
  70. package/dist/esm-node/manifest.mjs +117 -0
  71. package/dist/esm-node/plugin/compiler.mjs +67 -0
  72. package/dist/esm-node/plugin/create.mjs +228 -0
  73. package/dist/esm-node/plugin/index.mjs +103 -0
  74. package/dist/esm-node/plugin/profile.mjs +53 -0
  75. package/dist/esm-node/plugin/source.mjs +7 -0
  76. package/dist/esm-node/typecheck.mjs +560 -0
  77. package/dist/types/bootstrap.d.ts +10 -0
  78. package/dist/types/client.d.ts +40 -0
  79. package/dist/types/entry-application.d.ts +19 -0
  80. package/dist/types/entry-client.d.ts +10 -0
  81. package/dist/types/entry-server.d.ts +14 -0
  82. package/dist/types/federation-context.d.ts +28 -0
  83. package/dist/types/federation.d.ts +20 -0
  84. package/dist/types/i18n/I18nProvider.d.ts +8 -0
  85. package/dist/types/i18n/LocalizedLink.d.ts +14 -0
  86. package/dist/types/i18n/context.d.ts +9 -0
  87. package/dist/types/i18n/index.d.ts +9 -0
  88. package/dist/types/i18n/languageSync.d.ts +26 -0
  89. package/dist/types/i18n/types.d.ts +34 -0
  90. package/dist/types/i18n/useI18n.d.ts +21 -0
  91. package/dist/types/i18n-external-snapshot.d.ts +43 -0
  92. package/dist/types/index.d.ts +9 -0
  93. package/dist/types/manifest.d.ts +34 -0
  94. package/dist/types/plugin/compiler-manifest.d.cts +10 -0
  95. package/dist/types/plugin/compiler.d.ts +5 -0
  96. package/dist/types/plugin/create.d.ts +23 -0
  97. package/dist/types/plugin/index.d.ts +3 -0
  98. package/dist/types/plugin/profile.d.ts +2 -0
  99. package/dist/types/plugin/source.d.ts +1 -0
  100. package/dist/types/plugin/svg-component-template.d.cts +6 -0
  101. package/dist/types/router-client.d.ts +12 -0
  102. package/dist/types/router-external-snapshot.d.ts +10 -0
  103. package/dist/types/router-handler.d.ts +13 -0
  104. package/dist/types/router-server-cleanup.d.ts +3 -0
  105. package/dist/types/router-server-snapshot.d.ts +15 -0
  106. package/dist/types/router-server.d.ts +11 -0
  107. package/dist/types/router-snapshot.d.ts +14 -0
  108. package/dist/types/router.d.ts +30 -0
  109. package/dist/types/routes.d.ts +20 -0
  110. package/dist/types/server.d.ts +47 -0
  111. package/dist/types/typecheck.d.ts +31 -0
  112. package/dist/types/worker-federation-context.d.ts +34 -0
  113. package/dist/types/worker-federation-contexts.d.ts +25 -0
  114. package/dist/types/worker-federation.d.ts +20 -0
  115. package/package.json +278 -4
  116. package/src/bootstrap.ts +60 -0
  117. package/src/client.ts +568 -0
  118. package/src/entry-application.ts +76 -0
  119. package/src/entry-client.ts +90 -0
  120. package/src/entry-server.ts +153 -0
  121. package/src/federation-context.ts +53 -0
  122. package/src/federation.ts +307 -0
  123. package/src/i18n/I18nProvider.ts +47 -0
  124. package/src/i18n/LocalizedLink.ts +86 -0
  125. package/src/i18n/context.ts +12 -0
  126. package/src/i18n/index.ts +31 -0
  127. package/src/i18n/languageSync.ts +132 -0
  128. package/src/i18n/types.ts +36 -0
  129. package/src/i18n/useI18n.ts +119 -0
  130. package/src/i18n-external-snapshot.ts +803 -0
  131. package/src/index.ts +10 -0
  132. package/src/manifest.ts +253 -0
  133. package/src/plugin/compiler-manifest.cjs +204 -0
  134. package/src/plugin/compiler.ts +104 -0
  135. package/src/plugin/create.ts +273 -0
  136. package/src/plugin/index.ts +107 -0
  137. package/src/plugin/profile.ts +50 -0
  138. package/src/plugin/source.ts +16 -0
  139. package/src/plugin/svg-component-template.cjs +17 -0
  140. package/src/router-client.ts +144 -0
  141. package/src/router-external-snapshot.ts +363 -0
  142. package/src/router-handler.ts +185 -0
  143. package/src/router-server-cleanup.ts +11 -0
  144. package/src/router-server-snapshot.ts +564 -0
  145. package/src/router-server.ts +21 -0
  146. package/src/router-snapshot.ts +251 -0
  147. package/src/router.ts +192 -0
  148. package/src/routes.ts +274 -0
  149. package/src/server.ts +340 -0
  150. package/src/typecheck.ts +723 -0
  151. package/src/worker-federation-context.ts +51 -0
  152. package/src/worker-federation-contexts.ts +91 -0
  153. package/src/worker-federation.ts +120 -0
  154. package/README.md +0 -3
@@ -0,0 +1,273 @@
1
+ export type OctaneAppSourceOptions = {
2
+ appId: string;
3
+ title: string;
4
+ entryName?: string;
5
+ appRole?: 'shell' | 'vertical';
6
+ sourceExtension: '.tsx' | '.tsrx';
7
+ jsxImportSource: string;
8
+ capabilities: {
9
+ ssr: boolean;
10
+ federation: boolean;
11
+ };
12
+ };
13
+
14
+ export type OctaneAppSourceArtifact = {
15
+ path: string;
16
+ content: string;
17
+ };
18
+
19
+ export type OctaneAppSources = {
20
+ sourceExtension: '.tsx';
21
+ jsxImportSource: 'octane';
22
+ artifacts: OctaneAppSourceArtifact[];
23
+ };
24
+
25
+ /** The renderer adapter owns entry generation, the document and hydration. */
26
+ export function generateOctaneAppSources(
27
+ options: OctaneAppSourceOptions,
28
+ ): OctaneAppSources {
29
+ if ((options.entryName ?? 'main') !== 'main') {
30
+ throw new Error(
31
+ 'The Octane app template supports the main entry only. The create-time workspace writer places src/modern-app-env.d.ts, src/routes/index.css and the package manifest at the app root regardless of entryName, so it cannot yet route a second entry into src/<entryName>/. Add that entry by hand after generation (mirror src/routes under src/<entryName>/routes) until an admitted native multi-entry create template exists; the native entry generator itself already supports arbitrary entry names.',
32
+ );
33
+ }
34
+ if (!options.appId.trim() || !options.title.trim()) {
35
+ throw new Error('The Octane app template requires an appId and title.');
36
+ }
37
+ if (options.sourceExtension !== '.tsx') {
38
+ throw new Error(
39
+ 'The Octane app template requires the admitted .tsx source profile. .tsrx generation requires an admitted native template profile.',
40
+ );
41
+ }
42
+ if (options.jsxImportSource !== 'octane') {
43
+ throw new Error(
44
+ 'The Octane app template requires jsxImportSource: "octane" from the admitted profile.',
45
+ );
46
+ }
47
+ if (
48
+ typeof options.capabilities.ssr !== 'boolean' ||
49
+ typeof options.capabilities.federation !== 'boolean'
50
+ ) {
51
+ throw new Error('The Octane app template requires admitted capabilities.');
52
+ }
53
+ const title = JSON.stringify(options.title);
54
+ const aboutTitle = JSON.stringify(`${options.title} - About`);
55
+ const appId = JSON.stringify(options.appId);
56
+ return {
57
+ sourceExtension: '.tsx',
58
+ jsxImportSource: 'octane',
59
+ artifacts: [
60
+ {
61
+ path: 'src/routes/layout.tsx',
62
+ content: `import { Link, Outlet } from '@modern-js/renderer-octane/router';
63
+ import Stable from '../components/stable';
64
+ import './index.css';
65
+
66
+ const appTitle = ${title};
67
+
68
+ export default function Layout() {
69
+ return (
70
+ <main data-testid="native-layout">
71
+ <header>
72
+ <p>{appTitle}</p>
73
+ <nav aria-label="Application">
74
+ <Link to="/">Home</Link>
75
+ <Link to="/about">About</Link>
76
+ ${options.capabilities.federation ? ' <Link to="/remotes">Federated applications</Link>\n' : ''}
77
+ </nav>
78
+ </header>
79
+ <Stable />
80
+ <Outlet />
81
+ </main>
82
+ );
83
+ }
84
+ `,
85
+ },
86
+ {
87
+ path: 'src/routes/page.tsx',
88
+ content: `import {
89
+ Link,
90
+ useLoaderData,
91
+ useOctaneRouteAction,
92
+ } from '@modern-js/renderer-octane/router';
93
+ import { useActionState } from 'octane';
94
+ import Counter from '../components/counter';
95
+
96
+ const appId = ${appId};
97
+ const appTitle = ${title};
98
+
99
+ export default function HomePage() {
100
+ const data = useLoaderData({ strict: false });
101
+ const submit = useOctaneRouteAction();
102
+ const [result, action, pending] = useActionState(submit, undefined);
103
+ return (
104
+ <section data-app-id={appId} data-testid="native-route" data-renderer="octane">
105
+ <h1>{appTitle}</h1>
106
+ <Counter />
107
+ <pre data-testid="native-loader-value">{JSON.stringify(data)}</pre>
108
+ <form action={action}>
109
+ <label>
110
+ Name <input name="name" />
111
+ </label>
112
+ <button type="submit" disabled={pending}>
113
+ Save
114
+ </button>
115
+ <button type="submit" name="intent" value="redirect">
116
+ Save and redirect
117
+ </button>
118
+ </form>
119
+ <pre data-testid="native-action-value">{JSON.stringify(result)}</pre>
120
+ <Link to="/about">Open native about route</Link>
121
+ </section>
122
+ );
123
+ }
124
+ `,
125
+ },
126
+ {
127
+ path: 'src/routes/page.head.ts',
128
+ content: `import type { FileSystemRouteModule } from '@modern-js/renderer-octane/router';
129
+
130
+ export const head: NonNullable<FileSystemRouteModule['head']> = () => ({
131
+ meta: [
132
+ { title: ${title} },
133
+ { name: 'description', content: 'Built with the native Octane renderer' },
134
+ ],
135
+ });
136
+ `,
137
+ },
138
+ {
139
+ path: 'src/routes/about/page.tsx',
140
+ content: `import { Link } from '@modern-js/renderer-octane/router';
141
+
142
+ export default function AboutPage() {
143
+ return (
144
+ <section data-testid="native-about">
145
+ <h1>Native Octane navigation</h1>
146
+ <Link to="/">Home</Link>
147
+ </section>
148
+ );
149
+ }
150
+ `,
151
+ },
152
+ {
153
+ path: 'src/routes/about/page.head.ts',
154
+ content: `import type { FileSystemRouteModule } from '@modern-js/renderer-octane/router';
155
+
156
+ export const head: NonNullable<FileSystemRouteModule['head']> = () => ({
157
+ meta: [
158
+ { title: ${aboutTitle} },
159
+ { name: 'description', content: 'Built with the native Octane renderer' },
160
+ ],
161
+ });
162
+ `,
163
+ },
164
+ {
165
+ path: 'src/components/counter.tsx',
166
+ content: `import { useState } from 'octane';
167
+
168
+ export default function Counter() {
169
+ const [count, setCount] = useState(0);
170
+ return (
171
+ <section data-testid="native-edited-component">
172
+ <span data-testid="native-hmr-marker">Counter before native edit</span>
173
+ <button type="button" onClick={() => setCount(value => value + 1)}>
174
+ Increment
175
+ </button>
176
+ <output data-testid="native-count">{count}</output>
177
+ </section>
178
+ );
179
+ }
180
+ `,
181
+ },
182
+ {
183
+ path: 'src/components/stable.tsx',
184
+ content: `import { useState } from 'octane';
185
+
186
+ export default function Stable() {
187
+ const [count, setCount] = useState(0);
188
+ return (
189
+ <section data-testid="native-unaffected-component">
190
+ <button type="button" onClick={() => setCount(value => value + 1)}>
191
+ Increment stable state
192
+ </button>
193
+ <output data-testid="native-unaffected-count">{count}</output>
194
+ </section>
195
+ );
196
+ }
197
+ `,
198
+ },
199
+ {
200
+ path: 'src/routes/page.data.ts',
201
+ content: `import type { DataHandlerInput } from '@modern-js/renderer-core/data';
202
+
203
+ export function loader({ request }: DataHandlerInput) {
204
+ const url = new URL(request.url);
205
+ if (url.searchParams.get('case') === 'not-found')
206
+ throw new Response('Native route not found', { status: 404 });
207
+ if (url.searchParams.get('case') === 'error')
208
+ throw new Error('Native loader failure');
209
+ if (url.searchParams.get('case') === 'redirect')
210
+ return new Response(null, { headers: { location: '/about' }, status: 302 });
211
+ return {
212
+ message: 'Native loader value',
213
+ privateValue: request.headers.get('x-conformance-private'),
214
+ };
215
+ }
216
+
217
+ export function action({ request }: DataHandlerInput) {
218
+ return request.formData().then(form => {
219
+ const name = form.get('name');
220
+ if (typeof name !== 'string' || name.trim().length === 0)
221
+ return Response.json(
222
+ { fieldErrors: { name: 'Name required' } },
223
+ { status: 422 },
224
+ );
225
+ if (form.get('intent') === 'redirect')
226
+ return new Response(null, {
227
+ headers: {
228
+ location: '/about',
229
+ 'set-cookie': 'conformance-saved=1; Path=/; SameSite=Lax',
230
+ },
231
+ status: 303,
232
+ });
233
+ if (form.get('intent') === 'throw') throw new Error('Native action failure');
234
+ return {
235
+ saved: name,
236
+ privateValue: request.headers.get('x-conformance-private'),
237
+ };
238
+ });
239
+ }
240
+ `,
241
+ },
242
+ {
243
+ path: 'src/routes/error.tsx',
244
+ content: `import type { ErrorRouteComponent } from '@modern-js/renderer-octane/router';
245
+
246
+ const ErrorBoundary: ErrorRouteComponent = props => (
247
+ <section data-testid="native-error">
248
+ <h1>Something went wrong</h1>
249
+ <output>
250
+ {props.error instanceof Error ? props.error.message : String(props.error)}
251
+ </output>
252
+ </section>
253
+ );
254
+
255
+ export default ErrorBoundary;
256
+ `,
257
+ },
258
+ {
259
+ path: 'src/routes/not-found.tsx',
260
+ content: `import type { NotFoundRouteComponent } from '@modern-js/renderer-octane/router';
261
+
262
+ const NotFound: NotFoundRouteComponent = () => (
263
+ <section data-testid="native-not-found">
264
+ <h1>Page not found</h1>
265
+ </section>
266
+ );
267
+
268
+ export default NotFound;
269
+ `,
270
+ },
271
+ ],
272
+ };
273
+ }
@@ -0,0 +1,107 @@
1
+ import path from 'node:path';
2
+ import {
3
+ defineRendererAdapter,
4
+ type NativeRendererAdapter,
5
+ nativeRendererDependencies,
6
+ } from '@modern-js/renderer-core/adapter';
7
+ import { compilerArtifacts } from '../manifest';
8
+ import { createOctaneCompilerPlugin, pluginSourceDirectory } from './compiler';
9
+ import { generateOctaneAppSources } from './create';
10
+ import { octaneProfile } from './profile';
11
+ import { assertOctaneEntrySource } from './source';
12
+
13
+ /** The build-side Octane renderer adapter UltraModern loads for `renderer: 'octane'`. */
14
+ export const rendererAdapter: NativeRendererAdapter<'octane'> =
15
+ defineRendererAdapter({
16
+ name: 'octane',
17
+ kind: 'native',
18
+ profile: octaneProfile,
19
+ routerFrameworks: ['octane'],
20
+ runtime: {
21
+ package: 'octane',
22
+ bootstrap: '@modern-js/renderer-octane',
23
+ entryClient: '@modern-js/renderer-octane/entry-client',
24
+ entryServer: '@modern-js/renderer-octane/entry-server',
25
+ router: '@modern-js/renderer-octane/router',
26
+ i18n: '@modern-js/renderer-octane/i18n',
27
+ manifest: '@modern-js/renderer-octane/manifest',
28
+ },
29
+ federation: {
30
+ library: 'module',
31
+ ssr: true,
32
+ shared: [
33
+ 'octane',
34
+ 'octane/',
35
+ 'octane/internal/context',
36
+ 'octane/internal/signal-read',
37
+ 'octane/signals',
38
+ '@octanejs/tanstack-router',
39
+ '@octanejs/tanstack-router/',
40
+ '@modern-js/renderer-octane',
41
+ '@modern-js/renderer-octane/',
42
+ '@modern-js/renderer-octane/router',
43
+ '@modern-js/renderer-octane/federation',
44
+ '@modern-js/renderer-core',
45
+ '@modern-js/renderer-core/',
46
+ '@tanstack/router-core',
47
+ '@tanstack/history',
48
+ 'seroval',
49
+ 'seroval-plugins',
50
+ ],
51
+ // A remote can use a native API the local app never imports. Publish
52
+ // these providers from the host so their private hook state stays local.
53
+ sharedByEnvironment: {
54
+ client: {
55
+ 'octane/internal/client': 'octane/internal/client',
56
+ 'octane/signals/client': 'octane/signals/client',
57
+ 'octane/profiling': 'octane/profiling',
58
+ 'octane/hydration/streamed-signals':
59
+ 'octane/hydration/streamed-signals',
60
+ '@modern-js/renderer-octane/client':
61
+ '@modern-js/renderer-octane/client',
62
+ },
63
+ server: {
64
+ octane: 'octane/server',
65
+ 'octane/server': 'octane/server',
66
+ 'octane/internal/server': 'octane/internal/server',
67
+ 'octane/signals/server': 'octane/signals/server',
68
+ },
69
+ },
70
+ },
71
+ worker: { nativeDocuments: true, rsc: false },
72
+ lazyStyles: 'document',
73
+ // Hydration bytes belong to this exact native client compilation.
74
+ entryClient: {
75
+ declarations: 'declare const __webpack_hash__: string;\n',
76
+ fields: { nativeHydrationBuildId: '__webpack_hash__' },
77
+ },
78
+ svgComponentTemplate: () =>
79
+ path.join(pluginSourceDirectory(), 'svg-component-template.cjs'),
80
+ compiler: createOctaneCompilerPlugin,
81
+ artifacts: compilerArtifacts,
82
+ assertSupportedSource: assertOctaneEntrySource,
83
+ create: {
84
+ templates: { federation: true, workers: true },
85
+ dependencies: (profile, { typescriptVersion }) => ({
86
+ frameworkDependencies: [
87
+ '@modern-js/renderer-core',
88
+ '@modern-js/renderer-octane',
89
+ ],
90
+ dependencies: nativeRendererDependencies(profile),
91
+ devDependencies: {
92
+ [profile.compiler.name]:
93
+ profile.dependencies[profile.compiler.name] ??
94
+ profile.compiler.version,
95
+ typescript: typescriptVersion,
96
+ },
97
+ typecheckCommand: 'octane-tsc --noEmit --project tsconfig.json',
98
+ tsconfig: {
99
+ tsrx: {
100
+ compiler: 'octane/compiler/volar',
101
+ platform: 'web',
102
+ },
103
+ },
104
+ }),
105
+ generateAppSources: generateOctaneAppSources,
106
+ },
107
+ });
@@ -0,0 +1,50 @@
1
+ import type { RendererBuildProfile } from '@modern-js/renderer-core/adapter';
2
+
3
+ export const octaneProfile: RendererBuildProfile<'octane'> = {
4
+ renderer: 'octane',
5
+ status: 'preview',
6
+ protocolVersion: 1,
7
+ minimumNode: '26.12.0',
8
+ hmr: {
9
+ editedBoundary: 'may-reset',
10
+ unaffectedComponents: 'preserved',
11
+ document: 'preserved',
12
+ roots: 'single',
13
+ cleanup: 'exactly-once',
14
+ },
15
+ compiler: { name: '@octanejs/rspack-plugin', version: '0.2.4' },
16
+ hydration: {
17
+ name: 'octane',
18
+ version: '0.13.8',
19
+ },
20
+ router: {
21
+ name: '@octanejs/tanstack-router',
22
+ version: '0.2.10',
23
+ coreName: '@tanstack/router-core',
24
+ coreVersion: '1.171.38',
25
+ },
26
+ sourceExtensions: ['.tsx', '.tsrx', '.ts', '.js'],
27
+ jsxImportSource: 'octane',
28
+ dependencies: {
29
+ '@tsrx/oxc': '0.20.0',
30
+ octane:
31
+ 'https://github.com/BleedingDev/octane/releases/download/ultramodern-octane-2383d2ab1e70/octane-0.13.8.tgz',
32
+ '@octanejs/tanstack-router':
33
+ 'https://github.com/BleedingDev/octane/releases/download/ultramodern-octane-2383d2ab1e70/octanejs-tanstack-router-0.2.10.tgz',
34
+ '@octanejs/rspack-plugin':
35
+ 'https://github.com/BleedingDev/octane/releases/download/ultramodern-octane-2383d2ab1e70/octanejs-rspack-plugin-0.2.4.tgz',
36
+ '@tanstack/router-core':
37
+ 'https://github.com/BleedingDev/router/releases/download/ultramodern-router-core-585d3233f7f5/tanstack-router-core-1.171.38.tgz',
38
+ '@modern-js/renderer-octane': '3.8.3',
39
+ seroval: '1.6.9',
40
+ 'seroval-plugins': '1.6.9',
41
+ },
42
+ capabilities: {
43
+ worker: true,
44
+ moduleFederation: true,
45
+ rsc: false,
46
+ ssg: true,
47
+ i18n: true,
48
+ svgComponent: true,
49
+ },
50
+ };
@@ -0,0 +1,16 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { octaneProfile } from './profile';
4
+
5
+ export async function assertOctaneEntrySource(
6
+ source: string | false | undefined,
7
+ ): Promise<void> {
8
+ if (
9
+ source &&
10
+ path.extname(source).toLowerCase() === '.jsx' &&
11
+ (await fs.stat(source)).isFile()
12
+ )
13
+ throw new Error(
14
+ `unsupported-renderer-capability: Octane does not support .jsx source: ${source}. Supported source extensions: ${octaneProfile.sourceExtensions.join(', ')}.`,
15
+ );
16
+ }
@@ -0,0 +1,17 @@
1
+ // Octane follows React host semantics: refs are props and inner markup is
2
+ // trusted HTML. Native attribute names and `class` pass through unchanged.
3
+ module.exports = ({
4
+ attributes,
5
+ markup,
6
+ origin,
7
+ }) => `/** @jsxImportSource octane */
8
+ // Generated by UltraModern from ${origin}. Do not edit.
9
+ import { trustHTML } from 'octane';
10
+
11
+ const attributes: { readonly [name: string]: string } = ${attributes};
12
+ const markup = trustHTML(${markup});
13
+
14
+ export default function SvgComponent(props: { readonly [name: string]: unknown }) {
15
+ return <svg {...attributes} {...props} dangerouslySetInnerHTML={markup} />;
16
+ }
17
+ `;
@@ -0,0 +1,144 @@
1
+ import { type AnyRouter, RouterProvider } from '@octanejs/tanstack-router';
2
+ import { hydrate } from '@octanejs/tanstack-router/ssr/client';
3
+ import {
4
+ createElement,
5
+ hookSlots,
6
+ initializeHydrationEventCapture,
7
+ useEffect,
8
+ } from 'octane';
9
+ import { octaneRouterSnapshotAdapter } from './router-snapshot';
10
+
11
+ export interface OctaneRouterHydrationOptions {
12
+ signal?: AbortSignal;
13
+ }
14
+
15
+ const hydration = new WeakMap<AnyRouter, Promise<void>>();
16
+
17
+ function abortReason(signal: AbortSignal): unknown {
18
+ return signal.reason === undefined
19
+ ? new DOMException('Octane router hydration aborted', 'AbortError')
20
+ : signal.reason;
21
+ }
22
+
23
+ function awaitHydration(
24
+ promise: Promise<void>,
25
+ signal?: AbortSignal,
26
+ ): Promise<void> {
27
+ if (!signal) return promise;
28
+ signal.throwIfAborted();
29
+ return new Promise((resolve, reject) => {
30
+ let settled = false;
31
+ const finish = (success: boolean, error?: unknown) => {
32
+ if (settled) return;
33
+ settled = true;
34
+ signal.removeEventListener('abort', onAbort);
35
+ if (success) resolve();
36
+ else reject(error);
37
+ };
38
+ const onAbort = () => finish(false, abortReason(signal));
39
+ signal.addEventListener('abort', onAbort, { once: true });
40
+ promise.then(
41
+ () => finish(true),
42
+ error => finish(false, error),
43
+ );
44
+ if (signal.aborted) onAbort();
45
+ });
46
+ }
47
+
48
+ interface ReactiveMatchIds {
49
+ get(): readonly string[];
50
+ subscribe(callback: () => void): { unsubscribe(): void };
51
+ }
52
+
53
+ function isReactiveMatchIds(value: unknown): value is ReactiveMatchIds {
54
+ return (
55
+ typeof value === 'object' &&
56
+ value !== null &&
57
+ typeof Reflect.get(value, 'get') === 'function' &&
58
+ typeof Reflect.get(value, 'subscribe') === 'function'
59
+ );
60
+ }
61
+
62
+ function waitForCommittedMatches(
63
+ router: AnyRouter,
64
+ signal?: AbortSignal,
65
+ ): Promise<void> {
66
+ signal?.throwIfAborted();
67
+ const matchesId = router.stores.ids;
68
+ if (matchesId.get().length > 0) return Promise.resolve();
69
+ if (!isReactiveMatchIds(matchesId)) {
70
+ throw new Error(
71
+ 'Octane router hydration requires its native reactive match store',
72
+ );
73
+ }
74
+ return new Promise((resolve, reject) => {
75
+ let subscription: ReturnType<typeof matchesId.subscribe> | undefined;
76
+ let settled = false;
77
+ const cleanup = () => {
78
+ subscription?.unsubscribe();
79
+ signal?.removeEventListener('abort', onAbort);
80
+ };
81
+ const finish = () => {
82
+ if (settled || matchesId.get().length === 0) return;
83
+ settled = true;
84
+ cleanup();
85
+ resolve();
86
+ };
87
+ const onAbort = () => {
88
+ if (settled) return;
89
+ settled = true;
90
+ cleanup();
91
+ if (signal) reject(abortReason(signal));
92
+ };
93
+ signal?.addEventListener('abort', onAbort, { once: true });
94
+ if (signal?.aborted) {
95
+ onAbort();
96
+ return;
97
+ }
98
+ subscription = matchesId.subscribe(finish);
99
+ // Native stores can notify synchronously while a subscription is installed.
100
+ if (settled) subscription.unsubscribe();
101
+ else finish();
102
+ });
103
+ }
104
+
105
+ /** Restore native SSR matches before Octane adopts the server's route ranges. */
106
+ export async function prepareOctaneRouterHydration(
107
+ router: AnyRouter,
108
+ options: OctaneRouterHydrationOptions = {},
109
+ ): Promise<void> {
110
+ initializeHydrationEventCapture();
111
+ options.signal?.throwIfAborted();
112
+ const adapters = router.options.serializationAdapters ?? [];
113
+ if (!adapters.includes(octaneRouterSnapshotAdapter)) {
114
+ const nativeOptions = {
115
+ ...router.options,
116
+ serializationAdapters: [octaneRouterSnapshotAdapter, ...adapters],
117
+ };
118
+ router.update(nativeOptions);
119
+ }
120
+ let pending = hydration.get(router);
121
+ if (!pending) {
122
+ pending = Promise.resolve(hydrate(router)).then(() => {});
123
+ // Native hydration can finish after this startup owner has been cancelled.
124
+ void pending.catch(() => {});
125
+ hydration.set(router, pending);
126
+ }
127
+ await awaitHydration(pending, options.signal);
128
+ await waitForCommittedMatches(router, options.signal);
129
+ options.signal?.throwIfAborted();
130
+ }
131
+
132
+ const routerHydrationEffectSlot = Symbol(hookSlots(1));
133
+
134
+ /** Adopt the native provider's ranges inside the admitted fragment host. */
135
+ export function OctaneRouterRoot({ router }: { router: AnyRouter }) {
136
+ useEffect(
137
+ () => {
138
+ window.$_TSR?.h();
139
+ },
140
+ [],
141
+ routerHydrationEffectSlot,
142
+ );
143
+ return createElement(RouterProvider, { router });
144
+ }