@bleedingdev/modern-js-renderer-core 0.0.0-bootstrap → 3.9.3-ultramodern.1

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 (147) hide show
  1. package/LICENSE +21 -0
  2. package/dist/cjs/abort.js +65 -0
  3. package/dist/cjs/adapter.js +62 -0
  4. package/dist/cjs/data/body.js +78 -0
  5. package/dist/cjs/data/client.js +409 -0
  6. package/dist/cjs/data/codec.js +168 -0
  7. package/dist/cjs/data/index.js +125 -0
  8. package/dist/cjs/data/private.js +77 -0
  9. package/dist/cjs/data/routes.js +149 -0
  10. package/dist/cjs/data/server.js +791 -0
  11. package/dist/cjs/data/static.js +110 -0
  12. package/dist/cjs/data/tree.js +231 -0
  13. package/dist/cjs/data/types.js +56 -0
  14. package/dist/cjs/document.js +184 -0
  15. package/dist/cjs/entry-client.js +142 -0
  16. package/dist/cjs/entry-server.js +253 -0
  17. package/dist/cjs/federation.js +80 -0
  18. package/dist/cjs/identity.js +120 -0
  19. package/dist/cjs/index.js +46 -0
  20. package/dist/cjs/localization.js +48 -0
  21. package/dist/cjs/router.js +293 -0
  22. package/dist/cjs/server/assets.js +56 -0
  23. package/dist/cjs/server/dispatch.js +351 -0
  24. package/dist/cjs/server/index.js +57 -0
  25. package/dist/cjs/server/types.js +18 -0
  26. package/dist/cjs/server/worker.js +73 -0
  27. package/dist/cjs/session/cache.js +172 -0
  28. package/dist/cjs/session/index.js +63 -0
  29. package/dist/cjs/session/request.js +269 -0
  30. package/dist/cjs/session/types.js +18 -0
  31. package/dist/esm/abort.mjs +27 -0
  32. package/dist/esm/adapter.mjs +21 -0
  33. package/dist/esm/data/body.mjs +40 -0
  34. package/dist/esm/data/client.mjs +368 -0
  35. package/dist/esm/data/codec.mjs +112 -0
  36. package/dist/esm/data/index.mjs +7 -0
  37. package/dist/esm/data/private.mjs +39 -0
  38. package/dist/esm/data/routes.mjs +108 -0
  39. package/dist/esm/data/server.mjs +723 -0
  40. package/dist/esm/data/static.mjs +51 -0
  41. package/dist/esm/data/tree.mjs +193 -0
  42. package/dist/esm/data/types.mjs +6 -0
  43. package/dist/esm/document.mjs +131 -0
  44. package/dist/esm/entry-client.mjs +104 -0
  45. package/dist/esm/entry-server.mjs +215 -0
  46. package/dist/esm/federation.mjs +36 -0
  47. package/dist/esm/identity.mjs +63 -0
  48. package/dist/esm/index.mjs +1 -0
  49. package/dist/esm/localization.mjs +10 -0
  50. package/dist/esm/router.mjs +225 -0
  51. package/dist/esm/server/assets.mjs +15 -0
  52. package/dist/esm/server/dispatch.mjs +307 -0
  53. package/dist/esm/server/index.mjs +3 -0
  54. package/dist/esm/server/types.mjs +0 -0
  55. package/dist/esm/server/worker.mjs +35 -0
  56. package/dist/esm/session/cache.mjs +116 -0
  57. package/dist/esm/session/index.mjs +3 -0
  58. package/dist/esm/session/request.mjs +231 -0
  59. package/dist/esm/session/types.mjs +0 -0
  60. package/dist/esm-node/abort.mjs +28 -0
  61. package/dist/esm-node/adapter.mjs +22 -0
  62. package/dist/esm-node/data/body.mjs +41 -0
  63. package/dist/esm-node/data/client.mjs +369 -0
  64. package/dist/esm-node/data/codec.mjs +113 -0
  65. package/dist/esm-node/data/index.mjs +8 -0
  66. package/dist/esm-node/data/private.mjs +40 -0
  67. package/dist/esm-node/data/routes.mjs +109 -0
  68. package/dist/esm-node/data/server.mjs +724 -0
  69. package/dist/esm-node/data/static.mjs +52 -0
  70. package/dist/esm-node/data/tree.mjs +194 -0
  71. package/dist/esm-node/data/types.mjs +7 -0
  72. package/dist/esm-node/document.mjs +132 -0
  73. package/dist/esm-node/entry-client.mjs +105 -0
  74. package/dist/esm-node/entry-server.mjs +216 -0
  75. package/dist/esm-node/federation.mjs +37 -0
  76. package/dist/esm-node/identity.mjs +64 -0
  77. package/dist/esm-node/index.mjs +2 -0
  78. package/dist/esm-node/localization.mjs +11 -0
  79. package/dist/esm-node/router.mjs +226 -0
  80. package/dist/esm-node/server/assets.mjs +16 -0
  81. package/dist/esm-node/server/dispatch.mjs +308 -0
  82. package/dist/esm-node/server/index.mjs +4 -0
  83. package/dist/esm-node/server/types.mjs +1 -0
  84. package/dist/esm-node/server/worker.mjs +36 -0
  85. package/dist/esm-node/session/cache.mjs +117 -0
  86. package/dist/esm-node/session/index.mjs +4 -0
  87. package/dist/esm-node/session/request.mjs +232 -0
  88. package/dist/esm-node/session/types.mjs +1 -0
  89. package/dist/types/abort.d.ts +6 -0
  90. package/dist/types/adapter.d.ts +195 -0
  91. package/dist/types/data/body.d.ts +2 -0
  92. package/dist/types/data/client.d.ts +17 -0
  93. package/dist/types/data/codec.d.ts +14 -0
  94. package/dist/types/data/index.d.ts +8 -0
  95. package/dist/types/data/private.d.ts +6 -0
  96. package/dist/types/data/routes.d.ts +28 -0
  97. package/dist/types/data/server.d.ts +59 -0
  98. package/dist/types/data/static.d.ts +31 -0
  99. package/dist/types/data/tree.d.ts +3 -0
  100. package/dist/types/data/types.d.ts +104 -0
  101. package/dist/types/document.d.ts +76 -0
  102. package/dist/types/entry-client.d.ts +53 -0
  103. package/dist/types/entry-server.d.ts +63 -0
  104. package/dist/types/federation.d.ts +56 -0
  105. package/dist/types/identity.d.ts +27 -0
  106. package/dist/types/index.d.ts +1 -0
  107. package/dist/types/localization.d.ts +58 -0
  108. package/dist/types/router.d.ts +201 -0
  109. package/dist/types/server/assets.d.ts +14 -0
  110. package/dist/types/server/dispatch.d.ts +11 -0
  111. package/dist/types/server/index.d.ts +4 -0
  112. package/dist/types/server/types.d.ts +64 -0
  113. package/dist/types/server/worker.d.ts +25 -0
  114. package/dist/types/session/cache.d.ts +25 -0
  115. package/dist/types/session/index.d.ts +4 -0
  116. package/dist/types/session/request.d.ts +8 -0
  117. package/dist/types/session/types.d.ts +48 -0
  118. package/package.json +159 -4
  119. package/src/abort.ts +40 -0
  120. package/src/adapter.ts +255 -0
  121. package/src/data/body.ts +43 -0
  122. package/src/data/client.ts +501 -0
  123. package/src/data/codec.ts +247 -0
  124. package/src/data/index.ts +8 -0
  125. package/src/data/private.ts +68 -0
  126. package/src/data/routes.ts +201 -0
  127. package/src/data/server.ts +977 -0
  128. package/src/data/static.ts +105 -0
  129. package/src/data/tree.ts +244 -0
  130. package/src/data/types.ts +98 -0
  131. package/src/document.ts +338 -0
  132. package/src/entry-client.ts +236 -0
  133. package/src/entry-server.ts +393 -0
  134. package/src/federation.ts +111 -0
  135. package/src/identity.ts +128 -0
  136. package/src/index.ts +7 -0
  137. package/src/localization.ts +77 -0
  138. package/src/router.ts +475 -0
  139. package/src/server/assets.ts +60 -0
  140. package/src/server/dispatch.ts +523 -0
  141. package/src/server/index.ts +16 -0
  142. package/src/server/types.ts +94 -0
  143. package/src/server/worker.ts +94 -0
  144. package/src/session/cache.ts +177 -0
  145. package/src/session/index.ts +11 -0
  146. package/src/session/request.ts +324 -0
  147. package/src/session/types.ts +60 -0
@@ -0,0 +1,216 @@
1
+ import "node:module";
2
+ import { untilAborted } from "./abort.mjs";
3
+ import { handleDataRequest, invokeRouteData, normalizeDataResult } from "./data/index.mjs";
4
+ import { assertRendererIdentity } from "./identity.mjs";
5
+ import { nativeI18nView } from "./localization.mjs";
6
+ import { isRoutedApplication, matchApplicationRouteIds, selectApplicationDataRoute } from "./router.mjs";
7
+ function serverRouteLoader(application) {
8
+ return async (route, input)=>{
9
+ const loader = application.dataModules[route.id]?.loader;
10
+ if (!loader) return {
11
+ kind: 'success',
12
+ value: void 0,
13
+ status: 200
14
+ };
15
+ return invokeRouteData(loader, input, {
16
+ production: 'development' !== process.env.NODE_ENV
17
+ });
18
+ };
19
+ }
20
+ function resolveDocumentPolicy(session, vary) {
21
+ if (!session.responsePolicy) session.resolveResponse({
22
+ kind: 'document',
23
+ status: 200,
24
+ headers: [
25
+ [
26
+ 'content-type',
27
+ 'text/html; charset=utf-8'
28
+ ],
29
+ ...vary?.length ? [
30
+ [
31
+ 'vary',
32
+ vary.join(', ')
33
+ ]
34
+ ] : []
35
+ ],
36
+ cache: {
37
+ mode: 'no-store'
38
+ }
39
+ });
40
+ }
41
+ const detectedVary = (language)=>language?.kind === 'language' ? language.vary : void 0;
42
+ function createNativeServerEntry(options, adapter) {
43
+ const rendererIdentity = Object.freeze({
44
+ ...options.identity
45
+ });
46
+ const { i18n } = options;
47
+ const own = (request, context)=>{
48
+ assertRendererIdentity(context.entry, rendererIdentity);
49
+ assertRendererIdentity(context.session.identity, rendererIdentity);
50
+ if (request !== context.session.request) throw new Error('Native request/session ownership mismatch');
51
+ };
52
+ const prepare = (request, context)=>{
53
+ own(request, context);
54
+ return adapter.document({
55
+ documentId: crypto.randomUUID(),
56
+ rootId: options.rootId ?? 'root',
57
+ ...void 0 === context.nonce ? {} : {
58
+ nonce: context.nonce
59
+ },
60
+ ...void 0 === context.assets ? {} : {
61
+ assets: context.assets
62
+ }
63
+ }, context.nativeManifest, rendererIdentity);
64
+ };
65
+ const routedApplication = async (signal)=>{
66
+ const application = await untilAborted(options.app(), signal);
67
+ if (!isRoutedApplication(application)) throw new Error('unsupported-renderer-capability: ssrByRouteIds requires native route matching.');
68
+ return application;
69
+ };
70
+ const nativeCSRRequestHandler = (request, context)=>adapter.run(context.session, async ()=>{
71
+ const document = prepare(request, context);
72
+ if (!i18n) {
73
+ resolveDocumentPolicy(context.session);
74
+ return adapter.renderCSR(context, document);
75
+ }
76
+ const language = i18n.resolveRequest(request);
77
+ if ('redirect' === language.kind) return adapter.respond(context.session, i18n.redirect(language.location));
78
+ resolveDocumentPolicy(context.session, detectedVary(language));
79
+ return adapter.renderCSR(context, {
80
+ ...document,
81
+ lang: language.language,
82
+ inlineData: [
83
+ i18n.handoff(language.language)
84
+ ]
85
+ });
86
+ });
87
+ const nativeMatchRouteIds = (request, context)=>adapter.run(context.session, async ()=>{
88
+ own(request, context);
89
+ const application = await routedApplication(context.session.signal);
90
+ const router = adapter.createRouter(application, {
91
+ identity: rendererIdentity,
92
+ loadRoute: serverRouteLoader(application),
93
+ request,
94
+ context: context.session.platform.bindings,
95
+ session: context.session,
96
+ ...i18n ? {
97
+ rewrite: i18n.rewrite(()=>i18n.resolveRequest(request).language)
98
+ } : {}
99
+ });
100
+ return matchApplicationRouteIds(router, new URL(request.url));
101
+ });
102
+ const nativeRequestHandler = (request, context)=>adapter.run(context.session, async ()=>{
103
+ const document = prepare(request, context);
104
+ const { session } = context;
105
+ const application = await untilAborted(options.app(), session.signal);
106
+ const routed = isRoutedApplication(application);
107
+ const nativeRequest = routed ? new Request(request, {
108
+ signal: session.signal
109
+ }) : request;
110
+ const language = i18n?.resolveRequest(nativeRequest);
111
+ if (!routed && i18n && language?.kind === 'redirect') return adapter.respond(session, i18n.redirect(language.location));
112
+ const localize = async (inlineData = [])=>{
113
+ if (!i18n || !language) return {
114
+ document,
115
+ localization: void 0
116
+ };
117
+ const instance = await untilAborted(i18n.create(language.language), session.signal);
118
+ return {
119
+ document: {
120
+ ...document,
121
+ lang: instance.language,
122
+ inlineData: [
123
+ ...inlineData,
124
+ i18n.handoff(instance.language, instance)
125
+ ]
126
+ },
127
+ localization: nativeI18nView(i18n, instance)
128
+ };
129
+ };
130
+ if (!routed) {
131
+ const localized = await localize(document.inlineData);
132
+ resolveDocumentPolicy(session, detectedVary(language));
133
+ return adapter.renderComponent(context, application.default, localized.document, localized.localization);
134
+ }
135
+ const bindings = session.platform.bindings;
136
+ const outcomes = [];
137
+ const outcomeRanks = [];
138
+ let routeOrder;
139
+ const addOutcome = (rank, outcome)=>{
140
+ const at = outcomeRanks.findIndex((other)=>other > rank);
141
+ const index = at < 0 ? outcomes.length : at;
142
+ outcomes.splice(index, 0, outcome);
143
+ outcomeRanks.splice(index, 0, rank);
144
+ };
145
+ const router = adapter.createRouter(application, {
146
+ identity: rendererIdentity,
147
+ loadRoute: serverRouteLoader(application),
148
+ request: nativeRequest,
149
+ context: bindings,
150
+ onOutcome: (routeId, outcome)=>{
151
+ if ('response' in outcome) {
152
+ routeOrder ??= matchApplicationRouteIds(router, new URL(nativeRequest.url));
153
+ const rank = routeOrder.indexOf(routeId);
154
+ addOutcome(rank < 0 ? 1 / 0 : rank, outcome);
155
+ } else if (outcome.completion) {
156
+ const completion = outcome.completion;
157
+ completion.catch((error)=>session.fail(error));
158
+ session.registerCleanup(()=>completion);
159
+ }
160
+ },
161
+ session,
162
+ ...void 0 === context.nonce ? {} : {
163
+ nonce: context.nonce
164
+ },
165
+ ...i18n && language ? {
166
+ rewrite: i18n.rewrite(()=>language.language)
167
+ } : {}
168
+ });
169
+ const dataResponse = await handleDataRequest({
170
+ request: nativeRequest,
171
+ identity: rendererIdentity,
172
+ context: bindings,
173
+ privateValues: [
174
+ context,
175
+ session,
176
+ session.platform,
177
+ bindings
178
+ ],
179
+ production: 'development' !== process.env.NODE_ENV,
180
+ selectRoute: (dataRequest, routeId, operation)=>selectApplicationDataRoute(router, dataRequest, routeId, operation, application.dataModules)
181
+ });
182
+ if (dataResponse) return adapter.respond(session, dataResponse);
183
+ if (i18n && language?.kind === 'redirect') return adapter.respond(session, i18n.redirect(language.location));
184
+ const vary = detectedVary(language);
185
+ if (vary?.length) addOutcome(1 / 0, await normalizeDataResult(new Response(null, {
186
+ headers: {
187
+ vary: vary.join(', ')
188
+ }
189
+ })));
190
+ const localized = await localize();
191
+ return adapter.renderRoutes({
192
+ request: nativeRequest,
193
+ context,
194
+ router,
195
+ outcomes,
196
+ document: localized.document,
197
+ forbiddenValues: [
198
+ context,
199
+ session,
200
+ session.platform,
201
+ bindings,
202
+ nativeRequest
203
+ ],
204
+ ...localized.localization ? {
205
+ i18n: localized.localization
206
+ } : {}
207
+ });
208
+ });
209
+ return {
210
+ rendererIdentity,
211
+ nativeRequestHandler,
212
+ nativeCSRRequestHandler,
213
+ nativeMatchRouteIds
214
+ };
215
+ }
216
+ export { createNativeServerEntry };
@@ -0,0 +1,37 @@
1
+ import "node:module";
2
+ function getFederationHost(binding) {
3
+ if (void 0 === binding) return;
4
+ if ('function' != typeof binding?.instance) throw new TypeError('The native federation host binding is invalid.');
5
+ const instance = binding.instance();
6
+ if (!instance || 'function' != typeof instance.loadRemote) throw new TypeError('The application federation runtime has not started.');
7
+ return instance;
8
+ }
9
+ async function loadFederatedModule(instance, id) {
10
+ if (!instance) throw new Error(`Cannot load ${id}: this application has no Module Federation runtime. Add module-federation.config.ts with its remotes.`);
11
+ const module = await instance.loadRemote(id);
12
+ if (!module) throw new Error(`Remote module ${id} is unavailable`);
13
+ return module;
14
+ }
15
+ function remoteBrowserAssets(instance, id) {
16
+ const target = instance.remoteHandler?.idToRemoteMap?.[id];
17
+ const loaded = target && instance.moduleCache?.get(target.name);
18
+ const snapshot = loaded && instance.snapshotHandler?.getGlobalRemoteInfo(loaded.remoteInfo).remoteSnapshot;
19
+ if (!snapshot || 'string' != typeof snapshot.publicPath || !snapshot.publicPath || 'string' != typeof snapshot.remoteEntry || !snapshot.remoteEntry) return;
20
+ const expose = snapshot.modules?.find((module)=>module.modulePath === target.expose);
21
+ if (!expose?.assets) return;
22
+ const publicPath = snapshot.publicPath;
23
+ const url = (file)=>/^(?:https?:)?\/\//iu.test(file) ? file : `${publicPath.replace(/\/$/u, '')}/${file.replace(/^\//u, '')}`;
24
+ return {
25
+ js: [
26
+ url(snapshot.remoteEntry),
27
+ ...(expose.assets.js?.sync ?? []).map(url)
28
+ ],
29
+ css: [
30
+ ...new Set([
31
+ ...expose.assets.css?.sync ?? [],
32
+ ...expose.assets.css?.async ?? []
33
+ ].map(url))
34
+ ]
35
+ };
36
+ }
37
+ export { getFederationHost, loadFederatedModule, remoteBrowserAssets };
@@ -0,0 +1,64 @@
1
+ import "node:module";
2
+ function resolveRenderer(value = 'react') {
3
+ if ('string' == typeof value && value.trim() === value && /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/u.test(value)) return value;
4
+ throw new Error(`Unsupported UltraModern renderer: ${String(value)}`);
5
+ }
6
+ function identityCacheKey(identity) {
7
+ resolveRenderer(identity.renderer);
8
+ for (const key of [
9
+ 'appId',
10
+ 'entryName',
11
+ 'buildId'
12
+ ])if ('string' != typeof identity[key] || 0 === identity[key].trim().length) throw new Error(`Renderer identity requires a nonempty ${key}`);
13
+ if (1 !== identity.protocolVersion) throw new Error('Unsupported renderer data protocol version');
14
+ return JSON.stringify([
15
+ identity.renderer,
16
+ identity.appId,
17
+ identity.entryName,
18
+ identity.protocolVersion,
19
+ identity.buildId
20
+ ]);
21
+ }
22
+ function assertRendererIdentity(actual, expected) {
23
+ if (identityCacheKey(actual) !== identityCacheKey(expected)) throw new Error('Renderer identity conflicts with the application build');
24
+ }
25
+ const IDENTITY_FIELDS = [
26
+ 'renderer',
27
+ 'appId',
28
+ 'entryName',
29
+ 'protocolVersion',
30
+ 'buildId'
31
+ ];
32
+ function readRendererIdentity(value, expected) {
33
+ if (!value || 'object' != typeof value || ![
34
+ Object.prototype,
35
+ null
36
+ ].includes(Object.getPrototypeOf(value)) || Object.keys(value).some((key)=>!IDENTITY_FIELDS.includes(key))) throw new Error('A renderer identity must contain only identity fields.');
37
+ const identity = value;
38
+ assertRendererIdentity(identity, expected);
39
+ return Object.freeze({
40
+ renderer: identity.renderer,
41
+ appId: identity.appId,
42
+ entryName: identity.entryName,
43
+ protocolVersion: identity.protocolVersion,
44
+ buildId: identity.buildId
45
+ });
46
+ }
47
+ function nativeModuleManifestFilename(renderer, entryName) {
48
+ if ('string' != typeof entryName || !entryName.trim()) throw new Error('Native module manifests require an application entry name.');
49
+ return `${resolveRenderer(renderer)}-module-manifest.${encodeURIComponent(entryName)}.json`;
50
+ }
51
+ const RENDERER_IDENTITY_HEADER = 'x-ultramodern-renderer-identity';
52
+ function serializeRendererIdentityHeader(identity) {
53
+ identityCacheKey(identity);
54
+ return JSON.stringify({
55
+ renderer: identity.renderer,
56
+ appId: identity.appId,
57
+ entryName: identity.entryName,
58
+ protocolVersion: identity.protocolVersion,
59
+ buildId: identity.buildId
60
+ }).replace(/[\u007f-\u{10ffff}]/gu, (character)=>Array.from({
61
+ length: character.length
62
+ }, (_, index)=>`\\u${character.charCodeAt(index).toString(16).padStart(4, '0')}`).join(''));
63
+ }
64
+ export { RENDERER_IDENTITY_HEADER, assertRendererIdentity, identityCacheKey, nativeModuleManifestFilename, readRendererIdentity, resolveRenderer, serializeRendererIdentityHeader };
@@ -0,0 +1,2 @@
1
+ import "node:module";
2
+ export { assertRendererIdentity, identityCacheKey, resolveRenderer } from "./identity.mjs";
@@ -0,0 +1,11 @@
1
+ import "node:module";
2
+ function nativeI18nView(i18n, instance) {
3
+ return {
4
+ instance,
5
+ languages: i18n.languages,
6
+ ...void 0 === i18n.localisedUrls ? {} : {
7
+ localisedUrls: i18n.localisedUrls
8
+ }
9
+ };
10
+ }
11
+ export { nativeI18nView };
@@ -0,0 +1,226 @@
1
+ import "node:module";
2
+ import { untilAborted } from "./abort.mjs";
3
+ import { DataProtocolError, parsePublicData, serializePublicData, toTanstackPath } from "./data/index.mjs";
4
+ class RouteDataError extends Error {
5
+ constructor(routeId, outcome){
6
+ super(outcome.error.message);
7
+ this.name = outcome.error.name;
8
+ this.routeId = routeId;
9
+ this.status = 'response' in outcome ? outcome.response.status : outcome.status;
10
+ this.data = outcome.data;
11
+ }
12
+ }
13
+ function resolveRouteData(native, routeId, outcome, project = (value)=>value) {
14
+ switch(outcome.kind){
15
+ case 'success':
16
+ return project(outcome.value);
17
+ case 'deferred':
18
+ {
19
+ const value = parsePublicData(serializePublicData(outcome.critical));
20
+ for (const [key, promise] of Object.entries(outcome.deferred))Object.defineProperty(value, key, {
21
+ value: promise,
22
+ enumerable: true,
23
+ writable: true,
24
+ configurable: true
25
+ });
26
+ return project(value);
27
+ }
28
+ case 'redirect':
29
+ throw native.redirect({
30
+ href: outcome.location,
31
+ ...'response' in outcome ? {
32
+ headers: outcome.response.headers
33
+ } : {},
34
+ statusCode: 'response' in outcome ? outcome.response.status : outcome.status
35
+ });
36
+ case 'not-found':
37
+ throw native.notFound({
38
+ data: project(outcome.value)
39
+ });
40
+ case 'error':
41
+ {
42
+ const error = project(outcome.error);
43
+ if (!error || 'object' != typeof error || !('name' in error) || 'string' != typeof error.name || !('message' in error) || 'string' != typeof error.message) throw new DataProtocolError('Invalid public route error');
44
+ throw Object.freeze(new RouteDataError(routeId, {
45
+ ...outcome,
46
+ error: {
47
+ name: error.name,
48
+ message: error.message
49
+ },
50
+ data: project(outcome.data)
51
+ }));
52
+ }
53
+ }
54
+ }
55
+ function splitFileSystemRoutes(routes) {
56
+ const roots = routes.filter((route)=>route.isRoot);
57
+ if (roots.length > 1) throw new Error('A filesystem route tree requires one application root');
58
+ const root = roots[0];
59
+ const ids = new Set();
60
+ const visit = (route, nested)=>{
61
+ if (nested && route.isRoot) throw new Error('An application root cannot be a child route');
62
+ if (ids.has(route.id)) throw new Error(`Duplicate filesystem route id: ${route.id}`);
63
+ ids.add(route.id);
64
+ for (const child of route.children)visit(child, true);
65
+ };
66
+ for (const route of routes)visit(route, false);
67
+ return {
68
+ root,
69
+ children: root ? [
70
+ ...root.children,
71
+ ...routes.filter((route)=>route !== root)
72
+ ] : routes
73
+ };
74
+ }
75
+ function nativeRoutePath(route) {
76
+ const path = route.index ? '/' : route.path;
77
+ return path ? {
78
+ path: toTanstackPath(path)
79
+ } : {
80
+ id: route.id
81
+ };
82
+ }
83
+ async function loadFileSystemRoute(route, loadRoute, options, { params, location, abortController }) {
84
+ const base = options.request?.url ?? ("u" < typeof window ? void 0 : window.location.href);
85
+ if (!base) throw new Error('A server route loader requires its request');
86
+ const owners = [
87
+ abortController.signal,
88
+ ...options.request ? [
89
+ options.request.signal
90
+ ] : [],
91
+ ...options.signal ? [
92
+ options.signal
93
+ ] : []
94
+ ];
95
+ const signal = owners.length > 1 ? AbortSignal.any(owners) : owners[0];
96
+ signal.throwIfAborted();
97
+ const request = new Request(new URL(location.publicHref, base), {
98
+ headers: options.request?.headers,
99
+ signal
100
+ });
101
+ const outcome = await untilAborted(loadRoute(route, {
102
+ request,
103
+ routeId: route.id,
104
+ params,
105
+ context: options.context
106
+ }), signal);
107
+ return {
108
+ outcome,
109
+ request,
110
+ signal
111
+ };
112
+ }
113
+ function matchApplicationRoutes(router, url) {
114
+ return router.matchRoutes(router.parseLocation({
115
+ href: url.pathname + url.search + url.hash,
116
+ pathname: url.pathname,
117
+ search: url.search,
118
+ hash: url.hash,
119
+ state: {
120
+ __TSR_index: 0
121
+ }
122
+ }));
123
+ }
124
+ function applicationRouteId(router, nativeRouteId) {
125
+ const staticData = router.routesById[nativeRouteId]?.options.staticData;
126
+ const id = staticData && 'object' == typeof staticData ? Reflect.get(staticData, 'ultramodernRouteId') : void 0;
127
+ return 'string' == typeof id ? id : void 0;
128
+ }
129
+ function matchApplicationRouteIds(router, url) {
130
+ return matchApplicationRoutes(router, url).flatMap((match)=>{
131
+ const id = applicationRouteId(router, match.routeId);
132
+ return void 0 === id ? [] : [
133
+ id
134
+ ];
135
+ });
136
+ }
137
+ function selectApplicationDataRoute(router, request, requestedRouteId, operation, handlers) {
138
+ const match = matchApplicationRoutes(router, new URL(request.url)).find((candidate)=>applicationRouteId(router, candidate.routeId) === requestedRouteId);
139
+ const handler = handlers[requestedRouteId]?.[operation];
140
+ if (!match || 'function' != typeof handler) return;
141
+ return {
142
+ routeId: requestedRouteId,
143
+ params: match.params,
144
+ handler
145
+ };
146
+ }
147
+ function isRoutedApplication(application) {
148
+ return 'routeIR' in application;
149
+ }
150
+ const viewFields = [
151
+ 'component',
152
+ 'pendingComponent',
153
+ 'errorComponent',
154
+ 'notFoundComponent'
155
+ ];
156
+ function resolveRouteSources(sources) {
157
+ const module = {};
158
+ for (const field of viewFields){
159
+ const source = sources[field];
160
+ if (source) module[field] = source.default;
161
+ }
162
+ if (sources.head) module.head = sources.head.head ?? sources.head.default;
163
+ if (sources.search) {
164
+ const validateSearch = sources.search.validateSearch ?? sources.search.default;
165
+ if ('function' != typeof validateSearch) throw new Error('A native search module must export validateSearch or a default validator');
166
+ module.validateSearch = validateSearch;
167
+ }
168
+ return module;
169
+ }
170
+ function createNativeRouter(application, options, factory) {
171
+ const context = options.context ?? {};
172
+ if (Object.hasOwn(context, 'ultramodern')) throw new Error('The native router context reserves ultramodern metadata');
173
+ const modules = Object.fromEntries(Object.entries(application.routeModules).map(([id, sources])=>[
174
+ id,
175
+ resolveRouteSources(sources)
176
+ ]));
177
+ let router;
178
+ const routeTree = factory.routeTree(application.routeIR, modules, {
179
+ ...options.request ? {
180
+ request: options.request
181
+ } : {},
182
+ ...options.signal ? {
183
+ signal: options.signal
184
+ } : {},
185
+ context,
186
+ ...options.onOutcome ? {
187
+ onOutcome: options.onOutcome
188
+ } : {},
189
+ ...options.session ? {
190
+ session: options.session
191
+ } : {},
192
+ getRouter: ()=>{
193
+ if (!router) throw new Error('The native router is not created yet');
194
+ return router;
195
+ },
196
+ loadRoute: options.loadRoute
197
+ });
198
+ const url = options.request ? new URL(options.request.url) : void 0;
199
+ router = factory.router({
200
+ routeTree,
201
+ basepath: application.basePath,
202
+ context: {
203
+ ultramodern: Object.freeze({
204
+ rendererIdentity: Object.freeze({
205
+ ...options.identity
206
+ })
207
+ })
208
+ },
209
+ ...options.rewrite ? {
210
+ rewrite: options.rewrite
211
+ } : {},
212
+ ...void 0 === options.nonce ? {} : {
213
+ ssr: {
214
+ nonce: options.nonce
215
+ }
216
+ },
217
+ ...url ? {
218
+ location: {
219
+ origin: url.origin,
220
+ href: url.pathname + url.search + url.hash
221
+ }
222
+ } : {}
223
+ });
224
+ return router;
225
+ }
226
+ export { RouteDataError, createNativeRouter, isRoutedApplication, loadFileSystemRoute, matchApplicationRouteIds, matchApplicationRoutes, nativeRoutePath, resolveRouteData, selectApplicationDataRoute, splitFileSystemRoutes, untilAborted };
@@ -0,0 +1,16 @@
1
+ import "node:module";
2
+ import { collectDocumentAssets } from "../document.mjs";
3
+ import { assertRendererIdentity } from "../identity.mjs";
4
+ const RENDERER_ASSET_MANIFEST_FILE = 'renderer-assets.json';
5
+ function validateNativeClientAssetManifest(value, expectedIdentity) {
6
+ if (!value || 'object' != typeof value || Array.isArray(value)) throw new Error('Missing native client asset manifest. Rebuild the application before serving.');
7
+ const manifest = value;
8
+ if ('ultramodern-renderer-assets' !== manifest.schema || 1 !== manifest.version || 'react' === expectedIdentity.renderer || manifest.renderer !== expectedIdentity.renderer) throw new Error('Native client asset manifest conflicts with the selected renderer or schema.');
9
+ const entry = manifest.entries?.[expectedIdentity.entryName];
10
+ if (!entry?.rendererIdentity || !Array.isArray(entry.assets)) throw new Error(`Native client asset manifest is missing entry ${expectedIdentity.entryName}.`);
11
+ assertRendererIdentity(entry.rendererIdentity, expectedIdentity);
12
+ const assets = collectDocumentAssets(entry.assets);
13
+ if (!assets.some((asset)=>"script" === asset.kind)) throw new Error(`Native client asset manifest has no application script for ${expectedIdentity.entryName}.`);
14
+ return assets;
15
+ }
16
+ export { RENDERER_ASSET_MANIFEST_FILE, validateNativeClientAssetManifest };