@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,117 @@
1
+ import { nativeModuleManifestFilename, readRendererIdentity } from "@modern-js/renderer-core/identity";
2
+ import { assertNativeHydrationBuildId, assertOctaneIdentity } from "./bootstrap.mjs";
3
+ const OCTANE_RUNTIME_VERSION = '0.13.8';
4
+ const OCTANE_COMPILER_VERSION = '0.2.4';
5
+ function octaneModuleManifestFileName(entryName) {
6
+ return nativeModuleManifestFilename('octane', entryName);
7
+ }
8
+ function record(value, fields) {
9
+ if (!value || 'object' != typeof value || Array.isArray(value)) throw new Error('Malformed Octane compiler manifest object.');
10
+ if (Object.keys(value).some((key)=>!fields.includes(key))) throw new Error('Octane compiler manifest contains an unknown field.');
11
+ return value;
12
+ }
13
+ function digest(value) {
14
+ return 'string' == typeof value && /^[a-f\d]{64}$/u.test(value);
15
+ }
16
+ function nonempty(value) {
17
+ return 'string' == typeof value && value.trim().length > 0 && !value.includes('\0');
18
+ }
19
+ function assetPath(value) {
20
+ return nonempty(value) && value.endsWith('.js') && !value.startsWith('/') && !value.includes('\\') && !value.split('/').some((part)=>'..' === part || '.' === part || !part);
21
+ }
22
+ const validatedManifests = new WeakMap();
23
+ function validateOctaneModuleManifest(value, expectedIdentity, expectedNativeHydrationBuildId) {
24
+ assertOctaneIdentity(expectedIdentity);
25
+ const cached = value && 'object' == typeof value ? validatedManifests.get(value) : void 0;
26
+ const manifest = cached ?? readOctaneModuleManifest(value);
27
+ readRendererIdentity(manifest.rendererIdentity, expectedIdentity);
28
+ if (void 0 !== expectedNativeHydrationBuildId) {
29
+ assertNativeHydrationBuildId(expectedNativeHydrationBuildId);
30
+ if (manifest.nativeHydrationBuildId !== expectedNativeHydrationBuildId) throw new Error('Octane native hydration build differs from the compiled client.');
31
+ }
32
+ if (!cached) {
33
+ validatedManifests.set(value, manifest);
34
+ validatedManifests.set(manifest, manifest);
35
+ }
36
+ return manifest;
37
+ }
38
+ function readOctaneModuleManifest(value) {
39
+ const manifest = record(value, [
40
+ 'schemaVersion',
41
+ 'renderer',
42
+ 'runtimeVersion',
43
+ 'compilerVersion',
44
+ 'rendererIdentity',
45
+ 'nativeHydrationBuildId',
46
+ 'sourceModules',
47
+ 'assets'
48
+ ]);
49
+ if (1 !== manifest.schemaVersion || 'octane' !== manifest.renderer || manifest.runtimeVersion !== OCTANE_RUNTIME_VERSION || manifest.compilerVersion !== OCTANE_COMPILER_VERSION) throw new Error('Octane compiler manifest ABI conflicts with the application.');
50
+ const rendererIdentity = readRendererIdentity(manifest.rendererIdentity, manifest.rendererIdentity);
51
+ const nativeHydrationBuildId = manifest.nativeHydrationBuildId;
52
+ assertNativeHydrationBuildId(nativeHydrationBuildId);
53
+ if (!Array.isArray(manifest.assets) || !manifest.assets.length) throw new Error('Octane compiler manifest has no emitted JavaScript closure.');
54
+ const assetNames = new Set();
55
+ const assets = manifest.assets.map((value)=>{
56
+ const asset = record(value, [
57
+ 'file',
58
+ 'sha256'
59
+ ]);
60
+ if (!assetPath(asset.file) || !digest(asset.sha256) || assetNames.has(asset.file)) throw new Error('Invalid Octane emitted asset digest.');
61
+ assetNames.add(asset.file);
62
+ return Object.freeze({
63
+ file: asset.file,
64
+ sha256: asset.sha256
65
+ });
66
+ });
67
+ if (!Array.isArray(manifest.sourceModules) || !manifest.sourceModules.length) throw new Error('Octane compiler manifest has no authenticated native sources.');
68
+ const resources = new Set();
69
+ const sourceModules = manifest.sourceModules.map((value)=>{
70
+ const source = record(value, [
71
+ 'resource',
72
+ 'canonicalId',
73
+ 'moduleId',
74
+ 'transformKind',
75
+ 'emittedSourceSha256',
76
+ 'assets'
77
+ ]);
78
+ if (!nonempty(source.resource) || source.resource.startsWith('/') || source.resource.includes('\\') || !nonempty(source.canonicalId) || !('string' == typeof source.moduleId && nonempty(source.moduleId) || 'number' == typeof source.moduleId && Number.isFinite(source.moduleId)) || !('compile' === source.transformKind || 'slots' === source.transformKind || 'client-only-stub' === source.transformKind) || !digest(source.emittedSourceSha256) || !Array.isArray(source.assets) || !source.assets.length || source.assets.some((file)=>'string' != typeof file || !assetNames.has(file)) || new Set(source.assets).size !== source.assets.length || resources.has(source.resource)) throw new Error('Invalid Octane native source-to-asset provenance.');
79
+ resources.add(source.resource);
80
+ return Object.freeze({
81
+ resource: source.resource,
82
+ canonicalId: source.canonicalId,
83
+ moduleId: source.moduleId,
84
+ transformKind: source.transformKind,
85
+ emittedSourceSha256: source.emittedSourceSha256,
86
+ assets: Object.freeze([
87
+ ...source.assets
88
+ ])
89
+ });
90
+ });
91
+ return Object.freeze({
92
+ schemaVersion: 1,
93
+ renderer: 'octane',
94
+ runtimeVersion: OCTANE_RUNTIME_VERSION,
95
+ compilerVersion: OCTANE_COMPILER_VERSION,
96
+ rendererIdentity,
97
+ nativeHydrationBuildId,
98
+ sourceModules: Object.freeze(sourceModules),
99
+ assets: Object.freeze(assets)
100
+ });
101
+ }
102
+ const compilerArtifacts = {
103
+ clientManifestFile: octaneModuleManifestFileName,
104
+ async validateClientManifest (value, identity, context) {
105
+ const hydrationBuildId = context.compilationHash ?? context.hydrationBuildId;
106
+ if (context.development && ('string' != typeof hydrationBuildId || !hydrationBuildId.trim())) throw new Error('Octane development snapshot has no native hydration build.');
107
+ const nativeManifest = validateOctaneModuleManifest(value, identity, hydrationBuildId);
108
+ return {
109
+ nativeManifest,
110
+ hydrationBuildId: nativeManifest.nativeHydrationBuildId
111
+ };
112
+ },
113
+ isMutableDevelopmentAsset (filename, entryNames) {
114
+ return 'octane-client-build.json' === filename || entryNames.some((entry)=>filename === octaneModuleManifestFileName(entry));
115
+ }
116
+ };
117
+ export { OCTANE_COMPILER_VERSION, OCTANE_RUNTIME_VERSION, compilerArtifacts, octaneModuleManifestFileName, validateOctaneModuleManifest };
@@ -0,0 +1,98 @@
1
+ import { RouterProvider } from "@octanejs/tanstack-router";
2
+ import { hydrate } from "@octanejs/tanstack-router/ssr/client";
3
+ import { createElement, hookSlots, initializeHydrationEventCapture, useEffect } from "octane";
4
+ import { octaneRouterSnapshotAdapter } from "./router-snapshot.mjs";
5
+ const hydration = new WeakMap();
6
+ function abortReason(signal) {
7
+ return void 0 === signal.reason ? new DOMException('Octane router hydration aborted', 'AbortError') : signal.reason;
8
+ }
9
+ function awaitHydration(promise, signal) {
10
+ if (!signal) return promise;
11
+ signal.throwIfAborted();
12
+ return new Promise((resolve, reject)=>{
13
+ let settled = false;
14
+ const finish = (success, error)=>{
15
+ if (settled) return;
16
+ settled = true;
17
+ signal.removeEventListener('abort', onAbort);
18
+ if (success) resolve();
19
+ else reject(error);
20
+ };
21
+ const onAbort = ()=>finish(false, abortReason(signal));
22
+ signal.addEventListener('abort', onAbort, {
23
+ once: true
24
+ });
25
+ promise.then(()=>finish(true), (error)=>finish(false, error));
26
+ if (signal.aborted) onAbort();
27
+ });
28
+ }
29
+ function isReactiveMatchIds(value) {
30
+ return 'object' == typeof value && null !== value && 'function' == typeof Reflect.get(value, 'get') && 'function' == typeof Reflect.get(value, 'subscribe');
31
+ }
32
+ function waitForCommittedMatches(router, signal) {
33
+ signal?.throwIfAborted();
34
+ const matchesId = router.stores.ids;
35
+ if (matchesId.get().length > 0) return Promise.resolve();
36
+ if (!isReactiveMatchIds(matchesId)) throw new Error('Octane router hydration requires its native reactive match store');
37
+ return new Promise((resolve, reject)=>{
38
+ let subscription;
39
+ let settled = false;
40
+ const cleanup = ()=>{
41
+ subscription?.unsubscribe();
42
+ signal?.removeEventListener('abort', onAbort);
43
+ };
44
+ const finish = ()=>{
45
+ if (settled || 0 === matchesId.get().length) return;
46
+ settled = true;
47
+ cleanup();
48
+ resolve();
49
+ };
50
+ const onAbort = ()=>{
51
+ if (settled) return;
52
+ settled = true;
53
+ cleanup();
54
+ if (signal) reject(abortReason(signal));
55
+ };
56
+ signal?.addEventListener('abort', onAbort, {
57
+ once: true
58
+ });
59
+ if (signal?.aborted) return void onAbort();
60
+ subscription = matchesId.subscribe(finish);
61
+ if (settled) subscription.unsubscribe();
62
+ else finish();
63
+ });
64
+ }
65
+ async function prepareOctaneRouterHydration(router, options = {}) {
66
+ initializeHydrationEventCapture();
67
+ options.signal?.throwIfAborted();
68
+ const adapters = router.options.serializationAdapters ?? [];
69
+ if (!adapters.includes(octaneRouterSnapshotAdapter)) {
70
+ const nativeOptions = {
71
+ ...router.options,
72
+ serializationAdapters: [
73
+ octaneRouterSnapshotAdapter,
74
+ ...adapters
75
+ ]
76
+ };
77
+ router.update(nativeOptions);
78
+ }
79
+ let pending = hydration.get(router);
80
+ if (!pending) {
81
+ pending = Promise.resolve(hydrate(router)).then(()=>{});
82
+ pending.catch(()=>{});
83
+ hydration.set(router, pending);
84
+ }
85
+ await awaitHydration(pending, options.signal);
86
+ await waitForCommittedMatches(router, options.signal);
87
+ options.signal?.throwIfAborted();
88
+ }
89
+ const routerHydrationEffectSlot = Symbol(hookSlots(1));
90
+ function OctaneRouterRoot({ router }) {
91
+ useEffect(()=>{
92
+ window.$_TSR?.h();
93
+ }, [], routerHydrationEffectSlot);
94
+ return createElement(RouterProvider, {
95
+ router
96
+ });
97
+ }
98
+ export { OctaneRouterRoot, prepareOctaneRouterHydration };
@@ -0,0 +1,244 @@
1
+ import { associateRouterRenderProfile, captureRouterRenderSnapshot, createRouterRenderRouteTree, defaultParseSearch, defaultStringifySearch, disposeRouterRenderSnapshot, getRouterRenderProfile, matchContext, registerRouterRenderProfile, restoreRouterRenderSnapshot, routerContext } from "@octanejs/tanstack-router";
2
+ import { hasUnchangedOctaneFileRouteHead } from "./routes.mjs";
3
+ import { OCTANE_WORKER_CONTEXT_KEYS, getOctaneWorkerRouterRewrite, registerOctaneWorkerContext } from "./worker-federation-contexts.mjs";
4
+ const OCTANE_FILE_ROUTE_SNAPSHOT_PROFILE = Object.freeze({
5
+ key: 'ultramodern.file-routes',
6
+ version: '1'
7
+ });
8
+ const origins = new WeakMap();
9
+ let profileRegistered = false;
10
+ function plainRecord(value) {
11
+ return null !== value && 'object' == typeof value && [
12
+ Object.prototype,
13
+ null
14
+ ].includes(Object.getPrototypeOf(value));
15
+ }
16
+ function record(value, fields) {
17
+ if (!plainRecord(value)) throw new TypeError('Worker router context requires a plain record');
18
+ for (const key of Reflect.ownKeys(value)){
19
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
20
+ if ('string' != typeof key || fields && !fields.includes(key) || !descriptor || !descriptor.enumerable || !('value' in descriptor)) throw new TypeError('Unknown Worker router context field or accessor');
21
+ }
22
+ return value;
23
+ }
24
+ function nonempty(value) {
25
+ if ('string' != typeof value || !value.trim() || value.length > 1024) throw new TypeError('Invalid Worker router identity');
26
+ return value;
27
+ }
28
+ function publicContext(value) {
29
+ const context = record(value, [
30
+ 'ultramodern'
31
+ ]);
32
+ const metadata = record(context.ultramodern, [
33
+ 'rendererIdentity'
34
+ ]);
35
+ const identity = record(metadata.rendererIdentity, [
36
+ 'renderer',
37
+ 'appId',
38
+ 'entryName',
39
+ 'protocolVersion',
40
+ 'buildId'
41
+ ]);
42
+ if ('octane' !== identity.renderer || 1 !== identity.protocolVersion) throw new TypeError('Unsupported Worker router identity');
43
+ return Object.freeze({
44
+ ultramodern: Object.freeze({
45
+ rendererIdentity: Object.freeze({
46
+ renderer: 'octane',
47
+ appId: nonempty(identity.appId),
48
+ entryName: nonempty(identity.entryName),
49
+ protocolVersion: 1,
50
+ buildId: nonempty(identity.buildId)
51
+ })
52
+ })
53
+ });
54
+ }
55
+ function localizedUrls(value) {
56
+ if (void 0 === value || 'boolean' == typeof value) return value;
57
+ const input = record(value);
58
+ const result = {};
59
+ for (const [path, translations] of Object.entries(input)){
60
+ const names = {};
61
+ for (const [language, target] of Object.entries(record(translations)))names[nonempty(language)] = nonempty(target);
62
+ result[path] = names;
63
+ }
64
+ return result;
65
+ }
66
+ function rewriteDescriptor(value) {
67
+ if (void 0 === value) return;
68
+ const input = record(value, [
69
+ 'language',
70
+ 'languages',
71
+ 'localisedUrls'
72
+ ]);
73
+ if (!Array.isArray(input.languages) || !input.languages.length || input.languages.length > 64) throw new TypeError('Invalid Worker router i18n languages');
74
+ const languages = input.languages.map(nonempty);
75
+ const language = nonempty(input.language);
76
+ if (new Set(languages).size !== languages.length || !languages.includes(language)) throw new TypeError('Invalid Worker router i18n language');
77
+ const urls = localizedUrls(input.localisedUrls);
78
+ return {
79
+ language,
80
+ languages,
81
+ ...void 0 === urls ? {} : {
82
+ localisedUrls: urls
83
+ }
84
+ };
85
+ }
86
+ function nativeRewriteDescriptor(router) {
87
+ if (!router.options.rewrite) return;
88
+ const known = getOctaneWorkerRouterRewrite()?.capture(router.options.rewrite);
89
+ if (!known) throw new TypeError('Custom router rewrite requires an exact Worker profile');
90
+ const descriptor = rewriteDescriptor(known);
91
+ if (!descriptor) throw new TypeError('Missing native router rewrite provenance');
92
+ return {
93
+ language: descriptor.language,
94
+ languages: [
95
+ ...descriptor.languages
96
+ ],
97
+ ...void 0 === descriptor.localisedUrls ? {} : {
98
+ localisedUrls: descriptor.localisedUrls
99
+ }
100
+ };
101
+ }
102
+ function validateDefaultSnapshot(snapshot) {
103
+ publicContext(snapshot.publicContext);
104
+ rewriteDescriptor(snapshot.i18n);
105
+ }
106
+ function assertFileRouteRenderHost(router, allowRewrite) {
107
+ if (router.options.parseSearch !== defaultParseSearch || router.options.stringifySearch !== defaultStringifySearch || !allowRewrite && router.options.rewrite || router.options.routeMasks?.length) throw new TypeError('Router render snapshot: authored router behavior requires an exact local profile');
108
+ const authoredRouting = [
109
+ 'caseSensitive',
110
+ 'validateSearch',
111
+ 'loaderDeps',
112
+ 'beforeLoad',
113
+ 'context',
114
+ 'onEnter',
115
+ 'onStay',
116
+ 'onLeave',
117
+ 'lazyFn',
118
+ "scripts",
119
+ 'headers',
120
+ 'onError',
121
+ 'parseParams',
122
+ 'stringifyParams'
123
+ ];
124
+ const seen = new Set();
125
+ const visit = (route, depth)=>{
126
+ if (seen.has(route) || seen.size >= 4096 || depth > 64) throw new TypeError('Router render snapshot: route geometry must be bounded and acyclic');
127
+ seen.add(route);
128
+ if (authoredRouting.some((key)=>void 0 !== Reflect.get(route.options, key)) || route.options.params?.parse || route.options.params?.stringify || route.options.search?.middlewares?.length || route.lazyFn || 'path' in route.options && 'id' in route.options || !hasUnchangedOctaneFileRouteHead(route)) throw new TypeError('Router render snapshot: authored route behavior requires an exact local profile');
129
+ for (const child of route.children ?? [])visit(child, depth + 1);
130
+ };
131
+ visit(router.options.routeTree, 0);
132
+ }
133
+ function ensureDefaultProfile() {
134
+ if (profileRegistered) return;
135
+ registerRouterRenderProfile({
136
+ id: OCTANE_FILE_ROUTE_SNAPSHOT_PROFILE,
137
+ assertHost: (router, snapshot)=>{
138
+ const origin = origins.get(router);
139
+ 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');
140
+ assertFileRouteRenderHost(router, void 0 !== snapshot.i18n);
141
+ validateDefaultSnapshot(snapshot);
142
+ if (JSON.stringify(nativeRewriteDescriptor(router)) !== JSON.stringify(snapshot.i18n)) throw new TypeError('Worker router rewrite differs from its native provenance');
143
+ },
144
+ createRoutingTree: (snapshot)=>{
145
+ validateDefaultSnapshot(snapshot);
146
+ return createRouterRenderRouteTree(snapshot.routes);
147
+ },
148
+ routingOptions: (snapshot)=>{
149
+ const defaults = {
150
+ parseSearch: defaultParseSearch,
151
+ stringifySearch: defaultStringifySearch
152
+ };
153
+ const i18n = rewriteDescriptor(snapshot.i18n);
154
+ if (!i18n) return defaults;
155
+ const codec = getOctaneWorkerRouterRewrite();
156
+ if (!codec) throw new TypeError('Missing owning Worker router i18n codec');
157
+ return {
158
+ ...defaults,
159
+ rewrite: codec.restore(i18n)
160
+ };
161
+ }
162
+ });
163
+ profileRegistered = true;
164
+ }
165
+ function associateOctaneFileRouteSnapshot(router) {
166
+ ensureDefaultProfile();
167
+ const context = publicContext(router.options.context);
168
+ origins.set(router, {
169
+ publicContext: context,
170
+ rewrite: router.options.rewrite
171
+ });
172
+ associateRouterRenderProfile(router, {
173
+ id: OCTANE_FILE_ROUTE_SNAPSHOT_PROFILE,
174
+ publicContext: context
175
+ });
176
+ }
177
+ function captureOctaneRouterSnapshot(router) {
178
+ const origin = origins.get(router);
179
+ const declared = getRouterRenderProfile(router);
180
+ if (origin && declared?.key === OCTANE_FILE_ROUTE_SNAPSHOT_PROFILE.key && declared.version === OCTANE_FILE_ROUTE_SNAPSHOT_PROFILE.version) {
181
+ const i18n = nativeRewriteDescriptor(router);
182
+ associateRouterRenderProfile(router, {
183
+ id: OCTANE_FILE_ROUTE_SNAPSHOT_PROFILE,
184
+ publicContext: origin.publicContext,
185
+ ...void 0 === i18n ? {} : {
186
+ i18n
187
+ }
188
+ });
189
+ }
190
+ return captureRouterRenderSnapshot(router);
191
+ }
192
+ let registrations = 0;
193
+ let releaseContexts;
194
+ function registerOctaneRouterWorkerContexts() {
195
+ ensureDefaultProfile();
196
+ if (!releaseContexts) {
197
+ const releaseRouter = registerOctaneWorkerContext(routerContext, {
198
+ key: OCTANE_WORKER_CONTEXT_KEYS.router,
199
+ encode: (value)=>{
200
+ if (!value) throw new TypeError('Missing native router provider');
201
+ return captureOctaneRouterSnapshot(value);
202
+ },
203
+ decode: (value, signal)=>restoreRouterRenderSnapshot(value, {
204
+ signal
205
+ }),
206
+ dispose: (value)=>{
207
+ if (value) disposeRouterRenderSnapshot(value);
208
+ },
209
+ isProvided: (value)=>void 0 !== value
210
+ });
211
+ try {
212
+ const releaseMatch = registerOctaneWorkerContext(matchContext, {
213
+ key: OCTANE_WORKER_CONTEXT_KEYS.match,
214
+ encode: nonempty,
215
+ decode: nonempty,
216
+ validate: (value, readContext)=>{
217
+ const selected = readContext(routerContext);
218
+ const router = selected.present ? selected.value : void 0;
219
+ const current = router?.state.matches.find((match)=>match.routeId === value);
220
+ 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');
221
+ },
222
+ isProvided: (value)=>void 0 !== value
223
+ });
224
+ releaseContexts = ()=>{
225
+ releaseMatch();
226
+ releaseRouter();
227
+ };
228
+ } catch (error) {
229
+ releaseRouter();
230
+ throw error;
231
+ }
232
+ }
233
+ registrations++;
234
+ let released = false;
235
+ return ()=>{
236
+ if (released) return;
237
+ released = true;
238
+ if (0 === --registrations) {
239
+ releaseContexts?.();
240
+ releaseContexts = void 0;
241
+ }
242
+ };
243
+ }
244
+ export { OCTANE_FILE_ROUTE_SNAPSHOT_PROFILE, associateOctaneFileRouteSnapshot, captureOctaneRouterSnapshot, registerOctaneRouterWorkerContexts };
@@ -0,0 +1,133 @@
1
+ import { createMemoryHistory } from "@octanejs/tanstack-router";
2
+ import { attachRouterServerSsrUtils, getNormalizedURL, getOrigin, normalizeSsrResponse } from "@octanejs/tanstack-router/ssr/server";
3
+ import { cleanupOctaneRouterSSR } from "./router-server-cleanup.mjs";
4
+ import { prepareOctaneRouterSerialization } from "./router-server-snapshot.mjs";
5
+ function withAbort(pending, signal) {
6
+ signal.throwIfAborted();
7
+ return new Promise((resolve, reject)=>{
8
+ let settled = false;
9
+ const finish = (success, value)=>{
10
+ if (settled) return;
11
+ settled = true;
12
+ signal.removeEventListener('abort', onAbort);
13
+ if (success) resolve(value);
14
+ else reject(value);
15
+ };
16
+ const onAbort = ()=>finish(false, signal.reason);
17
+ signal.addEventListener('abort', onAbort, {
18
+ once: true
19
+ });
20
+ pending.then((value)=>finish(true, value), (error)=>finish(false, error));
21
+ if (signal.aborted) onAbort();
22
+ });
23
+ }
24
+ const CSP_HEADERS = new Set([
25
+ 'content-security-policy',
26
+ 'content-security-policy-report-only',
27
+ 'link',
28
+ 'server-timing'
29
+ ]);
30
+ function appendHeaders(destination, value) {
31
+ if (null == value) return;
32
+ const source = value instanceof Headers ? value : new Headers(value);
33
+ for (const [key, item] of Headers.prototype.entries.call(source))if ('set-cookie' !== key) if (CSP_HEADERS.has(key)) destination.append(key, item);
34
+ else if ('vary' === key) {
35
+ const fields = new Map();
36
+ for (const field of `${destination.get('vary') ?? ''},${item}`.split(',')){
37
+ const name = field.trim();
38
+ if (name && !fields.has(name.toLowerCase())) fields.set(name.toLowerCase(), name);
39
+ }
40
+ destination.set('vary', [
41
+ ...fields.values()
42
+ ].join(', '));
43
+ } else destination.set(key, item);
44
+ for (const cookie of Headers.prototype.getSetCookie.call(source))destination.append('set-cookie', cookie);
45
+ }
46
+ function requestHeaders(router) {
47
+ const headers = new Headers({
48
+ 'content-type': 'text/html; charset=utf-8'
49
+ });
50
+ for (const match of router.stores.matches.get()){
51
+ const field = Object.getOwnPropertyDescriptor(match, 'headers');
52
+ if (field?.get || field?.set) throw new TypeError('Native Octane response headers must be owned values');
53
+ appendHeaders(headers, field?.value);
54
+ }
55
+ const result = router._serverResult;
56
+ if (result?.type === 'redirect') appendHeaders(headers, result.redirect.headers);
57
+ return headers;
58
+ }
59
+ function createOctaneRequestHandler({ createRouter, request, session, getRouterManifest, serialization }) {
60
+ return async (callback)=>{
61
+ session.signal.throwIfAborted();
62
+ const router = createRouter();
63
+ let responseOwnsCleanup = false;
64
+ try {
65
+ const manifest = getRouterManifest ? await withAbort(Promise.resolve(getRouterManifest()), session.signal) : void 0;
66
+ attachRouterServerSsrUtils({
67
+ router,
68
+ manifest
69
+ });
70
+ const guard = prepareOctaneRouterSerialization(router, session, {
71
+ ...serialization,
72
+ forbiddenValues: [
73
+ request,
74
+ ...serialization?.forbiddenValues ?? []
75
+ ]
76
+ });
77
+ const { url } = getNormalizedURL(request.url, 'http://localhost');
78
+ const href = url.href.replace(url.origin, '');
79
+ router.update({
80
+ ...router.options,
81
+ history: createMemoryHistory({
82
+ initialEntries: [
83
+ href
84
+ ]
85
+ }),
86
+ origin: router.options.origin ?? getOrigin(request)
87
+ });
88
+ await withAbort(router.load({
89
+ _signal: session.signal
90
+ }), session.signal);
91
+ guard.assertActive();
92
+ const loadResult = router._serverResult;
93
+ if (loadResult?.type === 'redirect') {
94
+ const resolved = loadResult.redirect;
95
+ const headers = requestHeaders(router);
96
+ return new Response(null, {
97
+ status: resolved.status,
98
+ statusText: resolved.statusText,
99
+ headers
100
+ });
101
+ }
102
+ guard.assertMatches();
103
+ const serverSsr = router.serverSsr;
104
+ if (!serverSsr) throw new Error('Native Octane router SSR state is required');
105
+ await withAbort(serverSsr.dehydrate({
106
+ signal: session.signal
107
+ }), session.signal);
108
+ guard.assertActive();
109
+ const responseHeaders = requestHeaders(router);
110
+ const rendered = Promise.resolve(callback({
111
+ request,
112
+ router,
113
+ responseHeaders
114
+ }));
115
+ const result = normalizeSsrResponse(await withAbort(rendered, session.signal).catch((error)=>{
116
+ if (session.signal.aborted) rendered.then((late)=>{
117
+ try {
118
+ normalizeSsrResponse(late).response.body?.cancel().catch(()=>{});
119
+ } catch {}
120
+ }, ()=>{});
121
+ throw error;
122
+ }));
123
+ responseOwnsCleanup = 'stream' === result.serverSsrCleanup;
124
+ return result.response;
125
+ } catch (error) {
126
+ session.fail(error);
127
+ throw error;
128
+ } finally{
129
+ if (!responseOwnsCleanup) cleanupOctaneRouterSSR(router);
130
+ }
131
+ };
132
+ }
133
+ export { createOctaneRequestHandler };
@@ -0,0 +1,8 @@
1
+ const released = new WeakSet();
2
+ function cleanupOctaneRouterSSR(router) {
3
+ const serverSsr = router.serverSsr;
4
+ if (!serverSsr || released.has(serverSsr)) return;
5
+ released.add(serverSsr);
6
+ serverSsr.cleanup();
7
+ }
8
+ export { cleanupOctaneRouterSSR };