@bleedingdev/modern-js-app-tools 3.5.0-ultramodern.30 → 3.5.0-ultramodern.32

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 (116) hide show
  1. package/dist/cjs/plugins/backend-federation/codegen.js +208 -0
  2. package/dist/cjs/plugins/backend-federation/config.js +217 -0
  3. package/dist/cjs/plugins/backend-federation/emit.js +139 -0
  4. package/dist/cjs/plugins/backendFederationBuild.js +4 -298
  5. package/dist/cjs/plugins/deploy/platforms/cloudflare/artifacts.js +127 -0
  6. package/dist/cjs/plugins/deploy/platforms/cloudflare/constants.js +137 -0
  7. package/dist/cjs/plugins/deploy/platforms/cloudflare/delivery-unit.js +118 -0
  8. package/dist/cjs/plugins/deploy/platforms/cloudflare/i18n-worker.js +135 -0
  9. package/dist/cjs/plugins/deploy/platforms/cloudflare/index.js +126 -0
  10. package/dist/cjs/plugins/deploy/platforms/cloudflare/security-policies.js +223 -0
  11. package/dist/cjs/plugins/deploy/platforms/cloudflare/types.js +18 -0
  12. package/dist/cjs/plugins/deploy/platforms/cloudflare/utils.js +66 -0
  13. package/dist/cjs/plugins/deploy/platforms/cloudflare/worker-manifest.js +178 -0
  14. package/dist/cjs/plugins/deploy/platforms/cloudflare/wrangler-config.js +189 -0
  15. package/dist/cjs/plugins/deploy/platforms/cloudflare-output-verifier/identity.js +83 -0
  16. package/dist/cjs/plugins/deploy/platforms/cloudflare-output-verifier/index.js +184 -0
  17. package/dist/cjs/plugins/deploy/platforms/cloudflare-output-verifier/issues.js +54 -0
  18. package/dist/cjs/plugins/deploy/platforms/cloudflare-output-verifier/manifest.js +83 -0
  19. package/dist/cjs/plugins/deploy/platforms/cloudflare-output-verifier/mutation-policy.js +158 -0
  20. package/dist/cjs/plugins/deploy/platforms/cloudflare-output-verifier/worker-bundles.js +137 -0
  21. package/dist/cjs/plugins/deploy/platforms/cloudflare-output-verifier.js +23 -363
  22. package/dist/cjs/plugins/deploy/platforms/cloudflare.js +2 -745
  23. package/dist/cjs/plugins/deploy/platforms/templates/cloudflare-entry.001-bootstrap-security.mjs +22 -0
  24. package/dist/cjs/plugins/deploy/platforms/templates/cloudflare-entry.002-assets-routes.mjs +0 -0
  25. package/dist/cjs/plugins/deploy/platforms/templates/cloudflare-entry.003-i18n-locales.mjs +0 -0
  26. package/dist/cjs/plugins/deploy/platforms/templates/cloudflare-entry.004-rendering-css.mjs +0 -0
  27. package/dist/cjs/plugins/deploy/platforms/templates/cloudflare-entry.005-worker-dispatch.mjs +0 -0
  28. package/dist/cjs/plugins/deploy/platforms/templates/cloudflare-entry.006-fetch-handler.mjs +32 -0
  29. package/dist/esm/plugins/backend-federation/codegen.mjs +157 -0
  30. package/dist/esm/plugins/backend-federation/config.mjs +141 -0
  31. package/dist/esm/plugins/backend-federation/emit.mjs +88 -0
  32. package/dist/esm/plugins/backendFederationBuild.mjs +1 -296
  33. package/dist/esm/plugins/deploy/platforms/cloudflare/artifacts.mjs +64 -0
  34. package/dist/esm/plugins/deploy/platforms/cloudflare/constants.mjs +33 -0
  35. package/dist/esm/plugins/deploy/platforms/cloudflare/delivery-unit.mjs +67 -0
  36. package/dist/esm/plugins/deploy/platforms/cloudflare/i18n-worker.mjs +97 -0
  37. package/dist/esm/plugins/deploy/platforms/cloudflare/index.mjs +78 -0
  38. package/dist/esm/plugins/deploy/platforms/cloudflare/security-policies.mjs +167 -0
  39. package/dist/esm/plugins/deploy/platforms/cloudflare/types.mjs +0 -0
  40. package/dist/esm/plugins/deploy/platforms/cloudflare/utils.mjs +12 -0
  41. package/dist/esm/plugins/deploy/platforms/cloudflare/worker-manifest.mjs +118 -0
  42. package/dist/esm/plugins/deploy/platforms/cloudflare/wrangler-config.mjs +135 -0
  43. package/dist/esm/plugins/deploy/platforms/cloudflare-output-verifier/identity.mjs +45 -0
  44. package/dist/esm/plugins/deploy/platforms/cloudflare-output-verifier/index.mjs +129 -0
  45. package/dist/esm/plugins/deploy/platforms/cloudflare-output-verifier/issues.mjs +10 -0
  46. package/dist/esm/plugins/deploy/platforms/cloudflare-output-verifier/manifest.mjs +45 -0
  47. package/dist/esm/plugins/deploy/platforms/cloudflare-output-verifier/mutation-policy.mjs +97 -0
  48. package/dist/esm/plugins/deploy/platforms/cloudflare-output-verifier/worker-bundles.mjs +68 -0
  49. package/dist/esm/plugins/deploy/platforms/cloudflare-output-verifier.mjs +1 -347
  50. package/dist/esm/plugins/deploy/platforms/cloudflare.mjs +1 -735
  51. package/dist/esm/plugins/deploy/platforms/templates/cloudflare-entry.001-bootstrap-security.mjs +22 -0
  52. package/dist/esm/plugins/deploy/platforms/templates/cloudflare-entry.002-assets-routes.mjs +0 -0
  53. package/dist/esm/plugins/deploy/platforms/templates/cloudflare-entry.003-i18n-locales.mjs +0 -0
  54. package/dist/esm/plugins/deploy/platforms/templates/cloudflare-entry.004-rendering-css.mjs +0 -0
  55. package/dist/esm/plugins/deploy/platforms/templates/cloudflare-entry.005-worker-dispatch.mjs +0 -0
  56. package/dist/esm/plugins/deploy/platforms/templates/cloudflare-entry.006-fetch-handler.mjs +32 -0
  57. package/dist/esm-node/plugins/backend-federation/codegen.mjs +158 -0
  58. package/dist/esm-node/plugins/backend-federation/config.mjs +142 -0
  59. package/dist/esm-node/plugins/backend-federation/emit.mjs +89 -0
  60. package/dist/esm-node/plugins/backendFederationBuild.mjs +1 -296
  61. package/dist/esm-node/plugins/deploy/platforms/cloudflare/artifacts.mjs +65 -0
  62. package/dist/esm-node/plugins/deploy/platforms/cloudflare/constants.mjs +34 -0
  63. package/dist/esm-node/plugins/deploy/platforms/cloudflare/delivery-unit.mjs +68 -0
  64. package/dist/esm-node/plugins/deploy/platforms/cloudflare/i18n-worker.mjs +98 -0
  65. package/dist/esm-node/plugins/deploy/platforms/cloudflare/index.mjs +79 -0
  66. package/dist/esm-node/plugins/deploy/platforms/cloudflare/security-policies.mjs +168 -0
  67. package/dist/esm-node/plugins/deploy/platforms/cloudflare/types.mjs +1 -0
  68. package/dist/esm-node/plugins/deploy/platforms/cloudflare/utils.mjs +13 -0
  69. package/dist/esm-node/plugins/deploy/platforms/cloudflare/worker-manifest.mjs +119 -0
  70. package/dist/esm-node/plugins/deploy/platforms/cloudflare/wrangler-config.mjs +136 -0
  71. package/dist/esm-node/plugins/deploy/platforms/cloudflare-output-verifier/identity.mjs +46 -0
  72. package/dist/esm-node/plugins/deploy/platforms/cloudflare-output-verifier/index.mjs +130 -0
  73. package/dist/esm-node/plugins/deploy/platforms/cloudflare-output-verifier/issues.mjs +11 -0
  74. package/dist/esm-node/plugins/deploy/platforms/cloudflare-output-verifier/manifest.mjs +46 -0
  75. package/dist/esm-node/plugins/deploy/platforms/cloudflare-output-verifier/mutation-policy.mjs +98 -0
  76. package/dist/esm-node/plugins/deploy/platforms/cloudflare-output-verifier/worker-bundles.mjs +69 -0
  77. package/dist/esm-node/plugins/deploy/platforms/cloudflare-output-verifier.mjs +1 -347
  78. package/dist/esm-node/plugins/deploy/platforms/cloudflare.mjs +1 -735
  79. package/dist/esm-node/plugins/deploy/platforms/templates/cloudflare-entry.001-bootstrap-security.mjs +22 -0
  80. package/dist/esm-node/plugins/deploy/platforms/templates/cloudflare-entry.002-assets-routes.mjs +0 -0
  81. package/dist/esm-node/plugins/deploy/platforms/templates/cloudflare-entry.003-i18n-locales.mjs +0 -0
  82. package/dist/esm-node/plugins/deploy/platforms/templates/cloudflare-entry.004-rendering-css.mjs +0 -0
  83. package/dist/esm-node/plugins/deploy/platforms/templates/cloudflare-entry.005-worker-dispatch.mjs +0 -0
  84. package/dist/esm-node/plugins/deploy/platforms/templates/cloudflare-entry.006-fetch-handler.mjs +32 -0
  85. package/dist/types/plugins/backend-federation/codegen.d.ts +82 -0
  86. package/dist/types/plugins/backend-federation/config.d.ts +103 -0
  87. package/dist/types/plugins/backend-federation/emit.d.ts +13 -0
  88. package/dist/types/plugins/backendFederationBuild.d.ts +2 -11
  89. package/dist/types/plugins/deploy/platforms/cloudflare/artifacts.d.ts +15 -0
  90. package/dist/types/plugins/deploy/platforms/cloudflare/constants.d.ts +23 -0
  91. package/dist/types/plugins/deploy/platforms/cloudflare/delivery-unit.d.ts +23 -0
  92. package/dist/types/plugins/deploy/platforms/cloudflare/i18n-worker.d.ts +21 -0
  93. package/dist/types/plugins/deploy/platforms/cloudflare/index.d.ts +2 -0
  94. package/dist/types/plugins/deploy/platforms/cloudflare/security-policies.d.ts +88 -0
  95. package/dist/types/plugins/deploy/platforms/cloudflare/types.d.ts +5 -0
  96. package/dist/types/plugins/deploy/platforms/cloudflare/utils.d.ts +6 -0
  97. package/dist/types/plugins/deploy/platforms/cloudflare/worker-manifest.d.ts +100 -0
  98. package/dist/types/plugins/deploy/platforms/cloudflare/wrangler-config.d.ts +39 -0
  99. package/dist/types/plugins/deploy/platforms/cloudflare-output-verifier/identity.d.ts +4 -0
  100. package/dist/types/plugins/deploy/platforms/cloudflare-output-verifier/index.d.ts +18 -0
  101. package/dist/types/plugins/deploy/platforms/cloudflare-output-verifier/issues.d.ts +14 -0
  102. package/dist/types/plugins/deploy/platforms/cloudflare-output-verifier/manifest.d.ts +2 -0
  103. package/dist/types/plugins/deploy/platforms/cloudflare-output-verifier/mutation-policy.d.ts +13 -0
  104. package/dist/types/plugins/deploy/platforms/cloudflare-output-verifier/worker-bundles.d.ts +16 -0
  105. package/dist/types/plugins/deploy/platforms/cloudflare-output-verifier.d.ts +1 -32
  106. package/dist/types/plugins/deploy/platforms/cloudflare.d.ts +1 -2
  107. package/dist/types/plugins/deploy/platforms/templates/cloudflare-entry.001-bootstrap-security.d.mts +1 -0
  108. package/dist/types/plugins/deploy/platforms/templates/cloudflare-entry.002-assets-routes.d.mts +1 -0
  109. package/dist/types/plugins/deploy/platforms/templates/cloudflare-entry.003-i18n-locales.d.mts +1 -0
  110. package/dist/types/plugins/deploy/platforms/templates/cloudflare-entry.004-rendering-css.d.mts +1 -0
  111. package/dist/types/plugins/deploy/platforms/templates/cloudflare-entry.005-worker-dispatch.d.mts +1 -0
  112. package/package.json +11 -11
  113. package/dist/cjs/plugins/deploy/platforms/templates/cloudflare-entry.mjs +0 -879
  114. package/dist/esm/plugins/deploy/platforms/templates/cloudflare-entry.mjs +0 -879
  115. package/dist/esm-node/plugins/deploy/platforms/templates/cloudflare-entry.mjs +0 -879
  116. /package/dist/types/plugins/deploy/platforms/templates/{cloudflare-entry.d.mts → cloudflare-entry.006-fetch-handler.d.mts} +0 -0
@@ -1,879 +0,0 @@
1
- const ASSETS_BINDING = 'ASSETS';
2
- const MODERN_WORKER_MANIFEST = p_workerManifest;
3
- const WORKER_MODULE_LOADERS = p_workerModuleLoaders;
4
- const workerModulePromises = new Map();
5
- const effectBffDispatcherPromises = new Map();
6
- let effectEdgeRuntimePromise;
7
- const remoteJsonPromises = new Map();
8
- const CORS_POLICY = MODERN_WORKER_MANIFEST.security?.cors || {};
9
- const ASSET_CORS_ENABLED = false !== CORS_POLICY.assets;
10
- const APP_CORS_ALLOWED_ORIGINS = (CORS_POLICY.allowedOrigins || []).map((origin)=>String(origin).toLowerCase());
11
- const APP_CORS_ALLOWED_METHODS = (CORS_POLICY.allowedMethods?.length ? CORS_POLICY.allowedMethods : [
12
- 'GET',
13
- 'HEAD',
14
- 'POST',
15
- 'PUT',
16
- 'PATCH',
17
- 'DELETE',
18
- 'OPTIONS'
19
- ]).join(', ');
20
- const APP_CORS_ALLOWED_HEADERS = (CORS_POLICY.allowedHeaders?.length ? CORS_POLICY.allowedHeaders : [
21
- '*'
22
- ]).join(', ');
23
- const ASSET_CORS_HEADERS = {
24
- 'access-control-allow-headers': '*',
25
- 'access-control-allow-methods': 'GET, HEAD, OPTIONS',
26
- 'access-control-allow-origin': '*'
27
- };
28
- globalThis.__dirname ??= '/';
29
- globalThis.__filename ??= '/index.js';
30
- function getAllowedAppCorsOrigin(request) {
31
- if (0 === APP_CORS_ALLOWED_ORIGINS.length) return null;
32
- const origin = request.headers.get('origin');
33
- if (!origin) return null;
34
- if (APP_CORS_ALLOWED_ORIGINS.includes('*')) return '*';
35
- return APP_CORS_ALLOWED_ORIGINS.includes(origin.toLowerCase()) ? origin : null;
36
- }
37
- function appendVaryOrigin(headers) {
38
- const vary = headers.get('vary');
39
- if (!vary) return void headers.set('vary', 'origin');
40
- const varyValues = vary.split(',').map((value)=>value.trim().toLowerCase());
41
- if (!varyValues.includes('origin')) headers.set('vary', `${vary}, origin`);
42
- }
43
- function withAppCorsHeaders(response, request) {
44
- const allowedOrigin = getAllowedAppCorsOrigin(request);
45
- if (!allowedOrigin) return response;
46
- const headers = new Headers(response.headers);
47
- if (!headers.has('access-control-allow-origin')) headers.set('access-control-allow-origin', allowedOrigin);
48
- if ('*' !== allowedOrigin) appendVaryOrigin(headers);
49
- return new Response(response.body, {
50
- headers,
51
- status: response.status,
52
- statusText: response.statusText
53
- });
54
- }
55
- function withAssetCorsHeaders(response) {
56
- if (!ASSET_CORS_ENABLED) return response;
57
- const headers = new Headers(response.headers);
58
- for (const [name, value] of Object.entries(ASSET_CORS_HEADERS))if (!headers.has(name)) headers.set(name, value);
59
- return new Response(response.body, {
60
- headers,
61
- status: response.status,
62
- statusText: response.statusText
63
- });
64
- }
65
- function setHeaderIfEnabled(headers, name, value) {
66
- if (false === value || 'string' != typeof value || '' === value.trim()) return;
67
- if (!headers.has(name)) headers.set(name, value);
68
- }
69
- function renderContentSecurityPolicy(directives) {
70
- return Object.entries(directives || {}).filter(([, values])=>Array.isArray(values) && values.length > 0).sort(([left], [right])=>left.localeCompare(right)).map(([name, values])=>`${name} ${values.join(' ')}`).join('; ');
71
- }
72
- function isHtmlResponse(response) {
73
- return (response.headers.get('content-type') || '').includes('text/html');
74
- }
75
- function matchesPreviewHostname(hostname, pattern) {
76
- const normalizedHostname = hostname.toLowerCase();
77
- const normalizedPattern = String(pattern || '').toLowerCase();
78
- if (!normalizedPattern) return false;
79
- if (normalizedPattern.startsWith('*.')) return normalizedHostname.endsWith(normalizedPattern.slice(1));
80
- return normalizedHostname === normalizedPattern;
81
- }
82
- function shouldNoindex(request, noindex) {
83
- if (!noindex || false === noindex) return false;
84
- const { hostname } = new URL(request.url);
85
- const normalizedHostname = hostname.toLowerCase();
86
- if (false !== noindex.localhost && ('localhost' === normalizedHostname || '127.0.0.1' === normalizedHostname || '[::1]' === normalizedHostname)) return true;
87
- if (false !== noindex.workersDev && normalizedHostname.endsWith('.workers.dev')) return true;
88
- return (noindex.previewHostnames || []).some((pattern)=>matchesPreviewHostname(normalizedHostname, pattern));
89
- }
90
- function withCloudflareSecurityHeaders(response, request) {
91
- const security = MODERN_WORKER_MANIFEST.security;
92
- if (!security || false === security.enabled) return response;
93
- const headers = new Headers(response.headers);
94
- const configuredHeaders = security.headers || {};
95
- setHeaderIfEnabled(headers, 'referrer-policy', configuredHeaders.referrerPolicy);
96
- setHeaderIfEnabled(headers, 'x-content-type-options', configuredHeaders.contentTypeOptions);
97
- setHeaderIfEnabled(headers, 'permissions-policy', configuredHeaders.permissionsPolicy);
98
- const csp = security.contentSecurityPolicy;
99
- const cspHeader = csp?.mode === 'enforce' ? 'content-security-policy' : 'content-security-policy-report-only';
100
- const cspValue = renderContentSecurityPolicy(csp?.directives);
101
- if (isHtmlResponse(response) && csp?.mode !== 'off' && cspValue && !headers.has(cspHeader)) headers.set(cspHeader, cspValue);
102
- if (shouldNoindex(request, security.noindex)) headers.set('x-robots-tag', 'noindex, nofollow');
103
- return new Response(response.body, {
104
- headers,
105
- status: response.status,
106
- statusText: response.statusText
107
- });
108
- }
109
- function createRenderableRequest(request) {
110
- if ('HEAD' !== request.method) return request;
111
- return new Request(request, {
112
- method: 'GET'
113
- });
114
- }
115
- function finalizeResponseForRequest(response, request) {
116
- const securedResponse = withCloudflareSecurityHeaders(response, request);
117
- if ('HEAD' !== request.method) return securedResponse;
118
- const headers = new Headers(securedResponse.headers);
119
- headers.delete('content-length');
120
- return new Response(null, {
121
- headers,
122
- status: securedResponse.status,
123
- statusText: securedResponse.statusText
124
- });
125
- }
126
- function isFingerprintedAssetPathname(pathname) {
127
- return /(?:^|\/)[^/]+\.[a-f0-9]{8,}\.(?:css|js|mjs|json|svg|png|jpe?g|webp|avif|gif|woff2?|ttf)$/iu.test(pathname);
128
- }
129
- function withAssetHeaders(response, request) {
130
- const corsResponse = withAssetCorsHeaders(response);
131
- const headers = new Headers(corsResponse.headers);
132
- const { pathname } = new URL(request.url);
133
- if (isFingerprintedAssetPathname(pathname)) headers.set('cache-control', 'public, max-age=31536000, immutable');
134
- return new Response(corsResponse.body, {
135
- headers,
136
- status: corsResponse.status,
137
- statusText: corsResponse.statusText
138
- });
139
- }
140
- async function createCorsPreflightResponse(request, env) {
141
- if ('OPTIONS' !== request.method) return null;
142
- const allowedOrigin = getAllowedAppCorsOrigin(request);
143
- if (allowedOrigin) {
144
- const headers = new Headers({
145
- 'access-control-allow-headers': APP_CORS_ALLOWED_HEADERS,
146
- 'access-control-allow-methods': APP_CORS_ALLOWED_METHODS,
147
- 'access-control-allow-origin': allowedOrigin
148
- });
149
- if ('*' !== allowedOrigin) headers.set('vary', 'origin');
150
- return new Response(null, {
151
- headers,
152
- status: 204
153
- });
154
- }
155
- return null;
156
- }
157
- async function fetchAsset(request, env) {
158
- if ('GET' !== request.method && 'HEAD' !== request.method) return null;
159
- const assets = env?.[ASSETS_BINDING];
160
- if (!assets || 'function' != typeof assets.fetch) return null;
161
- const response = await assets.fetch(request);
162
- if (404 === response.status) return null;
163
- return withAssetHeaders(response, request);
164
- }
165
- async function fetchAssetByPath(pathname, request, env) {
166
- const url = new URL(request.url);
167
- url.pathname = `/${pathname.replace(/^\/+/u, '')}`;
168
- return fetchAsset(new Request(url, request), env);
169
- }
170
- async function fetchAssetByPathFollowingRedirects(pathname, request, env, visited = new Set()) {
171
- const normalizedPathname = pathname.startsWith('/') ? pathname : `/${pathname}`;
172
- if (visited.has(normalizedPathname)) return null;
173
- visited.add(normalizedPathname);
174
- const response = await fetchAssetByPath(normalizedPathname, request, env);
175
- if (response && response.status >= 300 && response.status < 400 && response.headers.has('location')) {
176
- const location = response.headers.get('location');
177
- if (location) {
178
- const nextUrl = new URL(location, request.url);
179
- const currentUrl = new URL(request.url);
180
- if (nextUrl.origin === currentUrl.origin) return fetchAssetByPathFollowingRedirects(nextUrl.pathname, request, env, visited);
181
- }
182
- }
183
- return response;
184
- }
185
- async function readAssetText(pathname, request, env) {
186
- const response = await fetchAssetByPathFollowingRedirects(pathname, request, env);
187
- if (!response || !response.ok) return;
188
- return response.text();
189
- }
190
- async function readAssetJson(pathname, request, env) {
191
- const text = await readAssetText(pathname, request, env);
192
- if (!text) return {};
193
- return JSON.parse(text);
194
- }
195
- function normalizeRoutePath(pathname) {
196
- if ('/' === pathname) return pathname;
197
- return pathname.replace(/\/+$/u, '');
198
- }
199
- function getPathExtension(pathname) {
200
- const lastSegment = pathname.split('/').pop() || '';
201
- const dotIndex = lastSegment.lastIndexOf('.');
202
- if (dotIndex <= 0 || dotIndex === lastSegment.length - 1) return '';
203
- return lastSegment.slice(dotIndex).toLowerCase();
204
- }
205
- function isAssetLikePathname(pathname) {
206
- const extension = getPathExtension(pathname);
207
- return '' !== extension;
208
- }
209
- function routeMatchesExactly(route, pathname) {
210
- if ('string' != typeof route?.urlPath) return false;
211
- return normalizeRoutePath(route.urlPath) === normalizeRoutePath(pathname);
212
- }
213
- function routeMatches(route, pathname) {
214
- if ('string' != typeof route.urlPath) return false;
215
- const routePath = normalizeRoutePath(route.urlPath);
216
- const requestPath = normalizeRoutePath(pathname);
217
- return routePath === requestPath || '/' === routePath && route.isSSR || requestPath.startsWith(`${routePath}/`);
218
- }
219
- function getRouteI18nEntry(route) {
220
- if (!route || 'string' != typeof route.entryName) return;
221
- return MODERN_WORKER_MANIFEST.i18n?.entries?.[route.entryName];
222
- }
223
- function getRouteBasePath(route) {
224
- const routePath = 'string' == typeof route?.urlPath ? normalizeRoutePath(route.urlPath) : '/';
225
- return routePath || '/';
226
- }
227
- function getRouteRemainingPathname(route, pathname) {
228
- const basePath = getRouteBasePath(route);
229
- if ('/' === basePath) return pathname || '/';
230
- if (!matchesPrefix(pathname, basePath)) return pathname || '/';
231
- const remaining = pathname.slice(basePath.length);
232
- return remaining ? remaining.startsWith('/') ? remaining : `/${remaining}` : '/';
233
- }
234
- function normalizeLanguage(value, languages) {
235
- if ('string' != typeof value || '' === value.trim()) return null;
236
- const normalized = value.trim().toLowerCase();
237
- const exact = languages.find((language)=>language.toLowerCase() === normalized);
238
- if (exact) return exact;
239
- const baseLanguage = normalized.split(/[-_]/u)[0];
240
- return languages.find((language)=>language.toLowerCase() === baseLanguage) || null;
241
- }
242
- function getLanguageFromPath(route, pathname, languages) {
243
- const remainingPathname = getRouteRemainingPathname(route, pathname);
244
- const firstSegment = remainingPathname.split('/').filter(Boolean)[0];
245
- return normalizeLanguage(firstSegment, languages);
246
- }
247
- function safeDecodeURIComponent(value) {
248
- try {
249
- return decodeURIComponent(value);
250
- } catch {
251
- return value;
252
- }
253
- }
254
- function detectLanguageFromCookie(cookieHeader, lookupCookie, languages) {
255
- if (!cookieHeader) return null;
256
- for (const cookiePart of cookieHeader.split(';')){
257
- const [rawName, ...rawValue] = cookiePart.trim().split('=');
258
- if (rawName === lookupCookie && 0 !== rawValue.length) return normalizeLanguage(safeDecodeURIComponent(rawValue.join('=')), languages);
259
- }
260
- return null;
261
- }
262
- function detectLanguageFromAcceptLanguage(acceptLanguage, languages) {
263
- if (!acceptLanguage) return null;
264
- return acceptLanguage.split(',').map((entry, index)=>{
265
- const [rawRange, ...rawParameters] = entry.trim().split(';');
266
- const qualityParameter = rawParameters.find((parameter)=>parameter.trim().startsWith('q='));
267
- const quality = qualityParameter ? Number.parseFloat(qualityParameter.split('=')[1] || '') : 1;
268
- return {
269
- index,
270
- language: normalizeLanguage(rawRange, languages),
271
- quality: Number.isFinite(quality) ? quality : 1
272
- };
273
- }).filter((candidate)=>candidate.language && candidate.quality > 0).sort((left, right)=>right.quality - left.quality || left.index - right.index)[0]?.language || null;
274
- }
275
- function detectRedirectLanguage(request, entry) {
276
- if (!entry.i18nextDetector) return null;
277
- const url = new URL(request.url);
278
- const detection = entry.detection || {};
279
- const order = Array.isArray(detection.order) ? detection.order : [
280
- 'querystring',
281
- 'cookie',
282
- 'header'
283
- ];
284
- for (const method of order){
285
- const language = 'querystring' === method ? normalizeLanguage(url.searchParams.get(detection.lookupQuerystring || 'lng'), entry.languages) : 'cookie' === method ? detectLanguageFromCookie(request.headers.get('cookie'), detection.lookupCookie || 'i18next', entry.languages) : 'header' === method ? detectLanguageFromAcceptLanguage(request.headers.get(detection.lookupHeader || 'accept-language'), entry.languages) : null;
286
- if (language) return language;
287
- }
288
- return null;
289
- }
290
- function normaliseSlashes(pathname) {
291
- const withoutDuplicateSlashes = pathname.replace(/\/+/g, '/');
292
- const withLeadingSlash = withoutDuplicateSlashes.startsWith('/') ? withoutDuplicateSlashes : `/${withoutDuplicateSlashes}`;
293
- return withLeadingSlash.length > 1 ? withLeadingSlash.replace(/\/+$/u, '') : withLeadingSlash;
294
- }
295
- function normalisePathPattern(pathname) {
296
- return normaliseSlashes(pathname).replace(/\[(.+?)\]/g, ':$1');
297
- }
298
- function stripLanguagePrefix(pathname, languages) {
299
- const segments = pathname.split('/').filter(Boolean);
300
- const firstSegment = segments[0];
301
- if (firstSegment && normalizeLanguage(firstSegment, languages)) return `/${segments.slice(1).join('/')}` || '/';
302
- return pathname || '/';
303
- }
304
- function escapeRegExp(value) {
305
- return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
306
- }
307
- function getPatternParamName(segment) {
308
- return segment.slice(1).replace(/\?$/, '');
309
- }
310
- function compilePathPattern(pattern) {
311
- const names = [];
312
- const segments = normalisePathPattern(pattern).split('/').filter(Boolean);
313
- const source = segments.map((segment)=>{
314
- if (segment.startsWith(':')) {
315
- names.push(getPatternParamName(segment));
316
- return segment.endsWith('?') ? '(?:/([^/]+))?' : '/([^/]+)';
317
- }
318
- if ('*' === segment) {
319
- names.push('*');
320
- return '/(.*)';
321
- }
322
- return `/${escapeRegExp(segment)}`;
323
- }).join('');
324
- return {
325
- names,
326
- regexp: new RegExp(`^${source || '/'}$`)
327
- };
328
- }
329
- function matchPathPattern(pathname, pattern) {
330
- const { names, regexp } = compilePathPattern(pattern);
331
- const match = regexp.exec(normaliseSlashes(pathname));
332
- if (!match) return null;
333
- const params = {};
334
- for(let index = 0; index < names.length; index++)try {
335
- params[names[index]] = decodeURIComponent(match[index + 1] || '');
336
- } catch {
337
- return null;
338
- }
339
- return params;
340
- }
341
- function buildPathFromPattern(pattern, params) {
342
- const path = normalisePathPattern(pattern).split('/').filter(Boolean).map((segment)=>{
343
- if (segment.startsWith(':')) {
344
- const param = params[getPatternParamName(segment)];
345
- return param ? encodeURIComponent(param) : '';
346
- }
347
- if ('*' === segment) return params['*'] || '';
348
- return segment;
349
- }).filter(Boolean).join('/');
350
- return `/${path}`;
351
- }
352
- function patternSpecificity(pattern) {
353
- const segments = normalisePathPattern(pattern).split('/').filter(Boolean);
354
- let staticSegments = 0;
355
- let dynamicSegments = 0;
356
- let splatSegments = 0;
357
- for (const segment of segments)if ('*' === segment) splatSegments++;
358
- else if (segment.startsWith(':')) dynamicSegments++;
359
- else staticSegments++;
360
- return {
361
- dynamicSegments,
362
- splatSegments,
363
- staticSegments,
364
- totalSegments: segments.length
365
- };
366
- }
367
- function comparePatternSpecificity(left, right) {
368
- const leftSpecificity = patternSpecificity(left);
369
- const rightSpecificity = patternSpecificity(right);
370
- return rightSpecificity.staticSegments - leftSpecificity.staticSegments || rightSpecificity.totalSegments - leftSpecificity.totalSegments || leftSpecificity.splatSegments - rightSpecificity.splatSegments || leftSpecificity.dynamicSegments - rightSpecificity.dynamicSegments;
371
- }
372
- function sortLocalisedPatterns(patterns) {
373
- return patterns.map((pattern, index)=>({
374
- pattern,
375
- index
376
- })).sort((left, right)=>comparePatternSpecificity(left.pattern.pattern, right.pattern.pattern) || left.index - right.index).map(({ pattern })=>pattern);
377
- }
378
- function resolveLocalisedPath(pathname, targetLanguage, languages, localisedUrls) {
379
- const normalizedPathname = normaliseSlashes(pathname);
380
- const canonicalCandidates = sortLocalisedPatterns(Object.entries(localisedUrls || {}).map(([canonicalPattern, localisedUrlEntry])=>({
381
- canonicalPattern,
382
- localisedUrlEntry,
383
- pattern: canonicalPattern
384
- })));
385
- for (const { canonicalPattern, localisedUrlEntry } of canonicalCandidates){
386
- const targetPattern = localisedUrlEntry[targetLanguage];
387
- const params = targetPattern ? matchPathPattern(normalizedPathname, canonicalPattern) : null;
388
- if (params) return buildPathFromPattern(targetPattern, params);
389
- }
390
- const localisedCandidates = sortLocalisedPatterns(Object.values(localisedUrls || {}).flatMap((localisedUrlEntry)=>{
391
- const targetPattern = localisedUrlEntry[targetLanguage];
392
- if (!targetPattern) return [];
393
- return languages.map((language)=>localisedUrlEntry[language]).filter(Boolean).map((sourcePattern)=>({
394
- pattern: sourcePattern,
395
- sourcePattern,
396
- targetPattern
397
- }));
398
- }));
399
- for (const { sourcePattern, targetPattern } of localisedCandidates){
400
- const params = matchPathPattern(normalizedPathname, sourcePattern);
401
- if (params) return buildPathFromPattern(targetPattern, params);
402
- }
403
- return normalizedPathname;
404
- }
405
- function localiseTargetPathname(pathname, language, languages, localisedUrls) {
406
- const pathWithoutLanguage = stripLanguagePrefix(pathname, languages);
407
- const resolvedPath = localisedUrls ? resolveLocalisedPath(pathWithoutLanguage, language, languages, localisedUrls) : pathWithoutLanguage;
408
- const resolvedSegments = resolvedPath.split('/').filter(Boolean);
409
- return `/${[
410
- language,
411
- ...resolvedSegments
412
- ].join('/')}`;
413
- }
414
- function shouldIgnoreLocaleRedirect(pathname, route, entry) {
415
- const remainingPathname = getRouteRemainingPathname(route, pathname);
416
- const normalizedPathname = remainingPathname.startsWith('/') ? remainingPathname : `/${remainingPathname}`;
417
- const ignoredPrefixes = [
418
- ...entry.ignoreRedirectRoutes || [],
419
- ...entry.staticRoutePrefixes || [],
420
- '/static',
421
- '/upload'
422
- ];
423
- return ignoredPrefixes.some((prefix)=>{
424
- const normalizedPrefix = normaliseSlashes(prefix);
425
- return normalizedPathname === normalizedPrefix || normalizedPathname.startsWith(`${normalizedPrefix}/`);
426
- });
427
- }
428
- function buildLocalizedUrl(route, request, language, entry) {
429
- const url = new URL(request.url);
430
- const basePath = getRouteBasePath(route);
431
- const remainingPathname = getRouteRemainingPathname(route, url.pathname);
432
- const localizedPathname = localiseTargetPathname(remainingPathname, language, entry.languages, entry.localisedUrls);
433
- const nextPathname = '/' === basePath ? localizedPathname : normaliseSlashes(`${basePath}/${localizedPathname}`);
434
- return `${nextPathname}${url.search}`;
435
- }
436
- function createLocaleRedirectResponse(location, entry) {
437
- const headers = new Headers({
438
- 'cache-control': 'private, no-store',
439
- location
440
- });
441
- const vary = [];
442
- const detectionOrder = entry.i18nextDetector ? entry.detection?.order || [] : [];
443
- if (detectionOrder.includes('header')) vary.push('Accept-Language');
444
- if (detectionOrder.includes('cookie')) vary.push('Cookie');
445
- if (vary.length > 0) headers.set('vary', vary.join(', '));
446
- return new Response(null, {
447
- headers,
448
- status: 302
449
- });
450
- }
451
- function createLocaleRedirectResponseForRequest(route, request) {
452
- const entry = getRouteI18nEntry(route);
453
- if (!entry || !Array.isArray(entry.languages) || 0 === entry.languages.length) return null;
454
- const { pathname, search } = new URL(request.url);
455
- if (isAssetLikePathname(pathname)) return null;
456
- if (shouldIgnoreLocaleRedirect(pathname, route, entry)) return null;
457
- const language = getLanguageFromPath(route, pathname, entry.languages);
458
- if (!language) {
459
- const targetLanguage = detectRedirectLanguage(request, entry) || entry.fallbackLanguage;
460
- return createLocaleRedirectResponse(buildLocalizedUrl(route, request, targetLanguage, entry), entry);
461
- }
462
- if (entry.localisedUrls) {
463
- const expectedUrl = buildLocalizedUrl(route, request, language, entry);
464
- if (expectedUrl !== `${pathname}${search}`) return createLocaleRedirectResponse(expectedUrl, entry);
465
- }
466
- return null;
467
- }
468
- function findRoute(request) {
469
- const { pathname } = new URL(request.url);
470
- const routes = MODERN_WORKER_MANIFEST.routeSpec.routes;
471
- return [
472
- ...routes
473
- ].sort((left, right)=>{
474
- const leftLength = left.urlPath?.length || 0;
475
- const rightLength = right.urlPath?.length || 0;
476
- return rightLength - leftLength;
477
- }).find((route)=>routeMatches(route, pathname));
478
- }
479
- async function fetchRouteHtml(route, request, env) {
480
- if (!route?.entryPath) return null;
481
- return fetchAssetByPath(route.entryPath, request, env);
482
- }
483
- function createNoopMonitors() {
484
- const noop = ()=>{};
485
- return {
486
- debug: noop,
487
- error: noop,
488
- info: noop,
489
- warn: noop
490
- };
491
- }
492
- function createRequestHandlerOptions({ route, htmlTemplate, routeManifest, loadableStats }) {
493
- const monitors = createNoopMonitors();
494
- return {
495
- resource: {
496
- route,
497
- routeManifest,
498
- loadableStats,
499
- htmlTemplate,
500
- entryName: route.entryName
501
- },
502
- params: {},
503
- loaderContext: {},
504
- config: {},
505
- locals: {},
506
- staticGenerate: false,
507
- monitors,
508
- onError (error) {
509
- monitors.error(error);
510
- },
511
- onTiming () {},
512
- reporter: {
513
- reportTiming: ()=>{}
514
- }
515
- };
516
- }
517
- function collectRouteCssAssets(route, routeManifest) {
518
- const routeAssets = routeManifest?.routeAssets || {};
519
- const candidateKeys = [
520
- route.entryName,
521
- `async-${route.entryName}`
522
- ].filter(Boolean);
523
- const assets = new Set();
524
- for (const key of candidateKeys){
525
- const routeAsset = routeAssets[key];
526
- const cssAssets = [
527
- ...Array.isArray(routeAsset?.referenceCssAssets) ? routeAsset.referenceCssAssets : [],
528
- ...Array.isArray(routeAsset?.assets) ? routeAsset.assets : []
529
- ];
530
- for (const asset of cssAssets)if ('string' == typeof asset && asset.endsWith('.css')) assets.add(asset);
531
- }
532
- return [
533
- ...assets
534
- ];
535
- }
536
- function collectRenderedFederatedExposes(html) {
537
- const renderedExposes = [];
538
- const tagPattern = /<[^>]*data-modern-(?:boundary-id|mf-expose)=["'][^"']+["'][^>]*>/g;
539
- const attributePattern = /\s(data-modern-(?:boundary-id|mf-expose))=["']([^"']+)["']/g;
540
- for (const [tag] of html.matchAll(tagPattern)){
541
- const attributes = {};
542
- for (const [, name, value] of tag.matchAll(attributePattern))attributes[name] = value;
543
- const boundaryId = attributes['data-modern-boundary-id'];
544
- const expose = attributes['data-modern-mf-expose'];
545
- if (boundaryId && expose) renderedExposes.push({
546
- boundaryId,
547
- expose
548
- });
549
- }
550
- return renderedExposes;
551
- }
552
- function getRemoteManifestUrl(remote, request) {
553
- const entry = remote?.entry;
554
- if ('string' != typeof entry || 0 === entry.length) return;
555
- return new URL(entry, request.url).toString();
556
- }
557
- function ensureTrailingSlash(value) {
558
- return value.endsWith('/') ? value : `${value}/`;
559
- }
560
- function getRemoteAssetBase(remoteManifest, manifestUrl) {
561
- const publicPath = 'string' == typeof remoteManifest?.metaData?.publicPath ? remoteManifest.metaData.publicPath : void 0;
562
- const fallbackBase = new URL('.', manifestUrl).toString();
563
- if (!publicPath || 'auto' === publicPath) return fallbackBase;
564
- try {
565
- return new URL(ensureTrailingSlash(publicPath), manifestUrl).toString();
566
- } catch {
567
- return fallbackBase;
568
- }
569
- }
570
- async function fetchRemoteJson(jsonUrl) {
571
- if (!remoteJsonPromises.has(jsonUrl)) remoteJsonPromises.set(jsonUrl, fetch(jsonUrl).then((response)=>{
572
- if (!response.ok) {
573
- remoteJsonPromises.delete(jsonUrl);
574
- return {};
575
- }
576
- return response.json().catch(()=>{
577
- remoteJsonPromises.delete(jsonUrl);
578
- return {};
579
- });
580
- }).catch(()=>{
581
- remoteJsonPromises.delete(jsonUrl);
582
- return {};
583
- }));
584
- return remoteJsonPromises.get(jsonUrl);
585
- }
586
- function findRemoteExpose(remoteManifest, exposePath) {
587
- const exposes = Array.isArray(remoteManifest?.exposes) ? remoteManifest.exposes : [];
588
- const normalizedExpose = exposePath.replace(/^\.\//u, '');
589
- return exposes.find((expose)=>{
590
- if (!expose || 'object' != typeof expose) return false;
591
- return expose.path === exposePath || expose.path === `./${normalizedExpose}` || expose.name === normalizedExpose;
592
- });
593
- }
594
- function collectCssAssetEntries(assets) {
595
- const cssAssets = assets?.css;
596
- return [
597
- ...Array.isArray(cssAssets?.sync) ? cssAssets.sync : [],
598
- ...Array.isArray(cssAssets?.async) ? cssAssets.async : []
599
- ].filter((asset)=>'string' == typeof asset && asset.endsWith('.css'));
600
- }
601
- function collectRouteManifestCssAssets(routeManifest) {
602
- const routeAssets = routeManifest?.routeAssets || {};
603
- const assets = new Set();
604
- for (const routeAsset of Object.values(routeAssets)){
605
- const cssAssets = [
606
- ...Array.isArray(routeAsset?.referenceCssAssets) ? routeAsset.referenceCssAssets : [],
607
- ...Array.isArray(routeAsset?.assets) ? routeAsset.assets : []
608
- ];
609
- for (const asset of cssAssets)if ('string' == typeof asset && asset.endsWith('.css')) assets.add(asset);
610
- }
611
- return [
612
- ...assets
613
- ];
614
- }
615
- async function collectRenderedRemoteCssHrefs(html, request, env) {
616
- const renderedExposes = collectRenderedFederatedExposes(html);
617
- if (0 === renderedExposes.length) return [];
618
- const hostManifest = await readAssetJson('mf-manifest.json', request, env);
619
- const remotes = Array.isArray(hostManifest?.remotes) ? hostManifest.remotes : [];
620
- const remoteByBoundary = new Map();
621
- const hrefs = new Set();
622
- for (const remote of remotes){
623
- if ('string' == typeof remote?.alias) remoteByBoundary.set(remote.alias, remote);
624
- if ('string' == typeof remote?.federationContainerName) remoteByBoundary.set(remote.federationContainerName, remote);
625
- }
626
- await Promise.all(renderedExposes.map(async ({ boundaryId, expose })=>{
627
- const remote = remoteByBoundary.get(boundaryId);
628
- const manifestUrl = remote ? getRemoteManifestUrl(remote, request) : void 0;
629
- if (!manifestUrl) return;
630
- const remoteManifest = await fetchRemoteJson(manifestUrl);
631
- const remoteExpose = findRemoteExpose(remoteManifest, expose);
632
- const publicPath = getRemoteAssetBase(remoteManifest, manifestUrl);
633
- const remoteRouteManifest = await fetchRemoteJson(new URL('routes-manifest.json', publicPath).toString());
634
- for (const asset of collectCssAssetEntries(remoteExpose?.assets))hrefs.add(new URL(asset, publicPath).toString());
635
- for (const asset of collectRouteManifestCssAssets(remoteRouteManifest))hrefs.add(new URL(asset, publicPath).toString());
636
- }));
637
- return [
638
- ...hrefs
639
- ];
640
- }
641
- function escapeAttribute(value) {
642
- return String(value).replace(/&/g, '&amp;').replace(/"/g, '&quot;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
643
- }
644
- function isAbsoluteUrl(value) {
645
- return /^[a-z][a-z\d+.-]*:/iu.test(value);
646
- }
647
- function toRouteCssHtmlHref(asset) {
648
- if (isAbsoluteUrl(asset) || asset.startsWith('/')) return asset;
649
- return `/${asset.replace(/^\/+/u, '')}`;
650
- }
651
- async function withRouteCssLinks(response, route, routeManifest, request, env) {
652
- const contentType = response.headers.get('content-type') || '';
653
- if (!contentType.includes('text/html')) return response;
654
- const html = await response.text();
655
- const cssEntries = [
656
- ...collectRouteCssAssets(route, routeManifest).map((asset)=>{
657
- const href = toRouteCssHtmlHref(asset);
658
- return {
659
- href,
660
- preloadHref: new URL(href, request.url).toString()
661
- };
662
- }),
663
- ...(await collectRenderedRemoteCssHrefs(html, request, env)).map((href)=>({
664
- href,
665
- preloadHref: new URL(href, request.url).toString()
666
- }))
667
- ];
668
- if (0 === cssEntries.length) return response;
669
- const seenCssHrefs = new Set();
670
- const uniqueCssEntries = cssEntries.filter((entry)=>{
671
- if (seenCssHrefs.has(entry.preloadHref)) return false;
672
- seenCssHrefs.add(entry.preloadHref);
673
- return true;
674
- });
675
- const headers = new Headers(response.headers);
676
- for (const { preloadHref } of uniqueCssEntries)headers.append('link', `<${preloadHref}>; rel=preload; as=style`);
677
- const links = uniqueCssEntries.filter(({ href, preloadHref })=>!html.includes(href) && !html.includes(preloadHref)).map(({ href })=>`<link rel="stylesheet" href="${escapeAttribute(href)}">`);
678
- if (0 === links.length || !html.includes('</head>')) return new Response(html, {
679
- headers,
680
- status: response.status,
681
- statusText: response.statusText
682
- });
683
- return new Response(html.replace('</head>', `${links.join('')}</head>`), {
684
- headers,
685
- status: response.status,
686
- statusText: response.statusText
687
- });
688
- }
689
- async function getRequestHandlerOptions(route, request, env) {
690
- const [htmlTemplate, routeManifest, loadableStats] = await Promise.all([
691
- readAssetText(route.entryPath, request, env),
692
- readAssetJson(MODERN_WORKER_MANIFEST.resources.routeManifest, request, env),
693
- readAssetJson(MODERN_WORKER_MANIFEST.resources.loadableStats, request, env)
694
- ]);
695
- return createRequestHandlerOptions({
696
- route,
697
- htmlTemplate: htmlTemplate || '',
698
- routeManifest,
699
- loadableStats
700
- });
701
- }
702
- async function loadWorkerModule(workerPath) {
703
- const loader = WORKER_MODULE_LOADERS[workerPath];
704
- if (!loader) return;
705
- if (!workerModulePromises.has(workerPath)) workerModulePromises.set(workerPath, loader());
706
- return workerModulePromises.get(workerPath);
707
- }
708
- async function loadEffectEdgeRuntime() {
709
- effectEdgeRuntimePromise ??= import('@modern-js/plugin-bff/effect-edge');
710
- return effectEdgeRuntimePromise;
711
- }
712
- function getRuntimeModule(workerModule) {
713
- const defaultExport = workerModule.default;
714
- const nestedDefaultExport = defaultExport && 'object' == typeof defaultExport ? defaultExport.default : void 0;
715
- return defaultExport && 'object' == typeof defaultExport ? {
716
- ...workerModule,
717
- ...defaultExport,
718
- ...nestedDefaultExport && 'object' == typeof nestedDefaultExport ? nestedDefaultExport : {}
719
- } : workerModule;
720
- }
721
- function getFetchHandler(workerModule) {
722
- const defaultExport = workerModule.default;
723
- const runtime = getRuntimeModule(workerModule);
724
- return 'function' == typeof runtime.fetch && runtime.fetch.bind(runtime) || 'function' == typeof defaultExport && defaultExport.fetch?.bind?.(defaultExport);
725
- }
726
- async function getRequestHandler(workerModule) {
727
- const defaultExport = workerModule.default;
728
- const runtime = getRuntimeModule(workerModule);
729
- return await workerModule.requestHandler || await runtime.requestHandler || ('function' == typeof defaultExport ? defaultExport : void 0);
730
- }
731
- async function dispatchRouteWorker(route, request, env, ctx) {
732
- const workerPath = route.worker;
733
- if (!workerPath) return new Response('Worker bundle not configured for SSR route', {
734
- status: 500,
735
- headers: {
736
- 'content-type': 'text/plain; charset=utf-8'
737
- }
738
- });
739
- const workerModule = await loadWorkerModule(workerPath);
740
- if (!workerModule) return new Response(`Worker bundle not found: ${workerPath}`, {
741
- status: 500,
742
- headers: {
743
- 'content-type': 'text/plain; charset=utf-8',
744
- 'x-modern-js-route-worker': workerPath
745
- }
746
- });
747
- const fetchHandler = getFetchHandler(workerModule);
748
- if (fetchHandler) return fetchHandler(request, env, ctx);
749
- const requestHandler = await getRequestHandler(workerModule);
750
- if ('function' == typeof requestHandler) {
751
- const requestHandlerOptions = await getRequestHandlerOptions(route, request, env);
752
- return withRouteCssLinks(await requestHandler(request, requestHandlerOptions), route, requestHandlerOptions.resource.routeManifest, request, env);
753
- }
754
- return new Response(`Worker bundle has no fetch or requestHandler export: ${workerPath}`, {
755
- status: 500,
756
- headers: {
757
- 'content-type': 'text/plain; charset=utf-8',
758
- 'x-modern-js-route-worker': workerPath
759
- }
760
- });
761
- }
762
- function matchesPrefix(pathname, prefix) {
763
- if (!prefix || '/' === prefix) return true;
764
- const normalized = prefix.endsWith('/') ? prefix.slice(0, -1) : prefix;
765
- return pathname === normalized || pathname.startsWith(`${normalized}/`);
766
- }
767
- function createRequestForMountedPrefix(request, prefix) {
768
- if (!prefix || '/' === prefix) return request;
769
- const url = new URL(request.url);
770
- const normalized = prefix.endsWith('/') ? prefix.slice(0, -1) : prefix;
771
- if (!matchesPrefix(url.pathname, normalized)) return request;
772
- const nextPath = url.pathname.slice(normalized.length) || '/';
773
- url.pathname = nextPath.startsWith('/') ? nextPath : `/${nextPath}`;
774
- return new Request(url, request);
775
- }
776
- async function dispatchBffRequest(request, env) {
777
- const bff = MODERN_WORKER_MANIFEST.bff;
778
- if (!bff?.worker || !matchesPrefix(new URL(request.url).pathname, bff.prefix)) return null;
779
- const workerModule = await loadWorkerModule(bff.worker);
780
- if (!workerModule) return new Response(`BFF worker bundle not found: ${bff.worker}`, {
781
- status: 500,
782
- headers: {
783
- 'content-type': 'text/plain; charset=utf-8',
784
- 'x-modern-js-bff-worker': bff.worker
785
- }
786
- });
787
- const mountedRequest = createRequestForMountedPrefix(request, bff.prefix);
788
- const defaultExport = workerModule.default;
789
- const runtime = getRuntimeModule(workerModule);
790
- if ('effect' === bff.runtimeFramework) {
791
- let effectDispatcherPromise = effectBffDispatcherPromises.get(bff.worker);
792
- if (!effectDispatcherPromise) {
793
- const { createEffectBffEdgeDispatcher } = await loadEffectEdgeRuntime();
794
- effectDispatcherPromise = createEffectBffEdgeDispatcher({
795
- module: runtime,
796
- prefix: bff.prefix
797
- });
798
- effectBffDispatcherPromises.set(bff.worker, effectDispatcherPromise);
799
- }
800
- const effectDispatcher = await effectDispatcherPromise;
801
- return effectDispatcher.dispatch(request, {
802
- env
803
- });
804
- }
805
- const directHandler = 'function' == typeof runtime.handler && runtime.handler || 'function' == typeof defaultExport && defaultExport;
806
- const createdHandler = 'function' == typeof runtime.createHandler ? runtime.createHandler().handler : void 0;
807
- const handler = directHandler || createdHandler;
808
- if ('function' != typeof handler) return new Response(`BFF worker bundle has no handler export: ${bff.worker}`, {
809
- status: 500,
810
- headers: {
811
- 'content-type': 'text/plain; charset=utf-8',
812
- 'x-modern-js-bff-worker': bff.worker
813
- }
814
- });
815
- const effectContext = {
816
- request: mountedRequest,
817
- env: env || {},
818
- path: new URL(request.url).pathname,
819
- method: request.method,
820
- operationContext: {
821
- request: mountedRequest,
822
- env: env || {},
823
- path: new URL(request.url).pathname,
824
- method: request.method
825
- }
826
- };
827
- return handler(mountedRequest, effectContext);
828
- }
829
- async function dispatchServiceBindingRequest(request, env) {
830
- const serviceBindings = MODERN_WORKER_MANIFEST.serviceBindings;
831
- if (!Array.isArray(serviceBindings) || 0 === serviceBindings.length) return null;
832
- const pathname = new URL(request.url).pathname;
833
- for (const binding of serviceBindings){
834
- if (!binding?.binding || !binding?.prefix) continue;
835
- if (!matchesPrefix(pathname, binding.prefix)) continue;
836
- const service = env?.[binding.binding];
837
- if (!service || 'function' != typeof service.fetch) return new Response(`Cloudflare service binding not available: ${binding.binding}`, {
838
- status: 502,
839
- headers: {
840
- 'content-type': 'text/plain; charset=utf-8',
841
- 'x-modern-js-service-binding': binding.binding
842
- }
843
- });
844
- return service.fetch(request);
845
- }
846
- return null;
847
- }
848
- export default {
849
- async fetch (request, env, ctx) {
850
- const corsPreflightResponse = await createCorsPreflightResponse(request, env);
851
- if (corsPreflightResponse) return finalizeResponseForRequest(corsPreflightResponse, request);
852
- const bffResponse = await dispatchBffRequest(request, env);
853
- if (bffResponse) return finalizeResponseForRequest(withAppCorsHeaders(bffResponse, request), request);
854
- const serviceBindingResponse = await dispatchServiceBindingRequest(request, env);
855
- if (serviceBindingResponse) return finalizeResponseForRequest(withAppCorsHeaders(serviceBindingResponse, request), request);
856
- const route = findRoute(request);
857
- const { pathname } = new URL(request.url);
858
- if (isAssetLikePathname(pathname) && !routeMatchesExactly(route, pathname)) {
859
- const assetResponse = await fetchAsset(request, env);
860
- if (assetResponse) return finalizeResponseForRequest(assetResponse, request);
861
- return finalizeResponseForRequest(withAppCorsHeaders(new Response('Not found', {
862
- status: 404
863
- }), request), request);
864
- }
865
- const localeRedirectResponse = createLocaleRedirectResponseForRequest(route, request);
866
- if (localeRedirectResponse) return finalizeResponseForRequest(withAppCorsHeaders(localeRedirectResponse, request), request);
867
- if (route?.worker) {
868
- const renderableRequest = createRenderableRequest(request);
869
- return finalizeResponseForRequest(withAppCorsHeaders(await dispatchRouteWorker(route, renderableRequest, env, ctx), request), request);
870
- }
871
- const htmlResponse = await fetchRouteHtml(route, request, env);
872
- if (htmlResponse) return finalizeResponseForRequest(htmlResponse, request);
873
- const assetResponse = await fetchAsset(request, env);
874
- if (assetResponse) return finalizeResponseForRequest(assetResponse, request);
875
- return finalizeResponseForRequest(withAppCorsHeaders(new Response('Not found', {
876
- status: 404
877
- }), request), request);
878
- }
879
- };