@bleedingdev/modern-js-runtime-extensions 3.9.0-ultramodern.9 → 3.9.3-ultramodern.2

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 (124) hide show
  1. package/dist/cjs/{reactContext.js → localisedUrls/config.js} +14 -10
  2. package/dist/cjs/localisedUrls/index.js +95 -0
  3. package/dist/cjs/localisedUrls/normalise.js +87 -0
  4. package/dist/cjs/localisedUrls/pathname.js +68 -0
  5. package/dist/cjs/localisedUrls/patterns.js +214 -0
  6. package/dist/cjs/localisedUrls/redirect.js +104 -0
  7. package/dist/cjs/localisedUrls/resolve.js +78 -0
  8. package/dist/cjs/localisedUrls/routeIdentity.js +92 -0
  9. package/dist/cjs/localisedUrls/routes.js +217 -0
  10. package/dist/cjs/localisedUrls/types.js +18 -0
  11. package/dist/cjs/rendererHead.js +18 -5
  12. package/dist/cjs/request-policy/policyCore.js +1 -1
  13. package/dist/cjs/request-policy/transport.js +2 -2
  14. package/dist/cjs/routerState.js +85 -77
  15. package/dist/cjs/routerStatePlugin.js +63 -9
  16. package/dist/cjs/server-config/serverSource.js +18 -0
  17. package/dist/esm/localisedUrls/config.mjs +11 -0
  18. package/dist/esm/localisedUrls/index.mjs +8 -0
  19. package/dist/esm/localisedUrls/normalise.mjs +37 -0
  20. package/dist/esm/localisedUrls/pathname.mjs +24 -0
  21. package/dist/esm/localisedUrls/patterns.mjs +152 -0
  22. package/dist/esm/localisedUrls/redirect.mjs +57 -0
  23. package/dist/esm/localisedUrls/resolve.mjs +37 -0
  24. package/dist/esm/localisedUrls/routeIdentity.mjs +54 -0
  25. package/dist/esm/localisedUrls/routes.mjs +176 -0
  26. package/dist/esm/localisedUrls/types.mjs +0 -0
  27. package/dist/esm/rendererHead.mjs +14 -5
  28. package/dist/esm/request-policy/policyCore.mjs +1 -1
  29. package/dist/esm/request-policy/transport.mjs +2 -2
  30. package/dist/esm/routerState.mjs +81 -73
  31. package/dist/esm/routerStatePlugin.mjs +62 -11
  32. package/dist/esm/server-config/serverSource.mjs +0 -0
  33. package/dist/esm-node/localisedUrls/config.mjs +12 -0
  34. package/dist/esm-node/localisedUrls/index.mjs +9 -0
  35. package/dist/esm-node/localisedUrls/normalise.mjs +38 -0
  36. package/dist/esm-node/localisedUrls/pathname.mjs +25 -0
  37. package/dist/esm-node/localisedUrls/patterns.mjs +153 -0
  38. package/dist/esm-node/localisedUrls/redirect.mjs +58 -0
  39. package/dist/esm-node/localisedUrls/resolve.mjs +38 -0
  40. package/dist/esm-node/localisedUrls/routeIdentity.mjs +55 -0
  41. package/dist/esm-node/localisedUrls/routes.mjs +177 -0
  42. package/dist/esm-node/localisedUrls/types.mjs +1 -0
  43. package/dist/esm-node/rendererHead.mjs +14 -5
  44. package/dist/esm-node/request-policy/policyCore.mjs +1 -1
  45. package/dist/esm-node/request-policy/transport.mjs +2 -2
  46. package/dist/esm-node/routerState.mjs +81 -73
  47. package/dist/esm-node/routerStatePlugin.mjs +62 -11
  48. package/dist/esm-node/server-config/serverSource.mjs +1 -0
  49. package/dist/types/helmetCompat.d.ts +1 -1
  50. package/dist/types/localisedUrls/config.d.ts +9 -0
  51. package/dist/types/localisedUrls/index.d.ts +10 -0
  52. package/dist/types/localisedUrls/normalise.d.ts +10 -0
  53. package/dist/types/localisedUrls/pathname.d.ts +4 -0
  54. package/dist/types/localisedUrls/patterns.d.ts +11 -0
  55. package/dist/types/localisedUrls/redirect.d.ts +19 -0
  56. package/dist/types/localisedUrls/resolve.d.ts +7 -0
  57. package/dist/types/localisedUrls/routeIdentity.d.ts +22 -0
  58. package/dist/types/localisedUrls/routes.d.ts +3 -0
  59. package/dist/types/localisedUrls/types.d.ts +14 -0
  60. package/dist/types/rendererHead.d.ts +5 -3
  61. package/dist/types/request-policy/types.d.ts +1 -1
  62. package/dist/types/routeHydration.d.ts +2 -2
  63. package/dist/types/routerProvider.d.ts +1 -3
  64. package/dist/types/routerState.d.ts +11 -6
  65. package/dist/types/routerStatePlugin.d.ts +12 -0
  66. package/dist/types/routerStateTypes.d.ts +47 -17
  67. package/dist/types/server-config/index.d.ts +1 -0
  68. package/dist/types/server-config/serverSource.d.ts +16 -0
  69. package/package.json +42 -44
  70. package/src/helmetCompat.ts +1 -1
  71. package/src/localisedUrls/config.ts +18 -0
  72. package/src/localisedUrls/index.ts +25 -0
  73. package/src/localisedUrls/normalise.ts +62 -0
  74. package/src/localisedUrls/pathname.ts +60 -0
  75. package/src/localisedUrls/patterns.ts +283 -0
  76. package/src/localisedUrls/redirect.ts +110 -0
  77. package/src/localisedUrls/resolve.ts +106 -0
  78. package/src/localisedUrls/routeIdentity.ts +83 -0
  79. package/src/localisedUrls/routes.ts +451 -0
  80. package/src/localisedUrls/types.ts +18 -0
  81. package/src/rendererHead.ts +25 -8
  82. package/src/request-policy/policyCore.ts +1 -1
  83. package/src/request-policy/transport.ts +2 -2
  84. package/src/request-policy/types.ts +1 -1
  85. package/src/routeHydration.ts +2 -2
  86. package/src/routerProvider.ts +1 -1
  87. package/src/routerState.ts +127 -115
  88. package/src/routerStatePlugin.ts +106 -10
  89. package/src/routerStateTypes.ts +47 -17
  90. package/src/server-config/index.ts +1 -0
  91. package/src/server-config/serverSource.ts +23 -0
  92. package/dist/cjs/buildIdentity.js +0 -73
  93. package/dist/esm/buildIdentity.mjs +0 -35
  94. package/dist/esm/reactContext.mjs +0 -7
  95. package/dist/esm-node/buildIdentity.mjs +0 -36
  96. package/dist/esm-node/reactContext.mjs +0 -8
  97. package/dist/types/buildIdentity.d.ts +0 -37
  98. package/dist/types/reactContext.d.ts +0 -6
  99. package/rslib.config.mts +0 -19
  100. package/rstest.config.mts +0 -38
  101. package/src/buildIdentity.ts +0 -61
  102. package/src/reactContext.ts +0 -16
  103. package/tests/buildIdentity.test.ts +0 -51
  104. package/tests/contextExtensions.test.ts +0 -27
  105. package/tests/headRuntime.test.tsx +0 -229
  106. package/tests/rendererHead.test.ts +0 -187
  107. package/tests/request-policy/browser-policy.test.ts +0 -108
  108. package/tests/request-policy/client-isolation.test.ts +0 -109
  109. package/tests/request-policy/node-policy.test.ts +0 -447
  110. package/tests/request-policy/policyCore.test.ts +0 -129
  111. package/tests/request-policy/requestContext.test.ts +0 -66
  112. package/tests/request-policy/requestFactory.outbound-contract.test.ts +0 -290
  113. package/tests/request-policy/traceparent.test.ts +0 -52
  114. package/tests/request-policy/transport-retry.test.ts +0 -367
  115. package/tests/routeHydration.test.ts +0 -180
  116. package/tests/routerPrefetchPolicy.test.ts +0 -102
  117. package/tests/routerProvider.test.ts +0 -29
  118. package/tests/routerState.test.ts +0 -78
  119. package/tests/routerStatePlugin.test.ts +0 -63
  120. package/tests/runtimeContextProjection.test.ts +0 -83
  121. package/tests/safeFailure.test.ts +0 -136
  122. package/tests/shellStream.test.ts +0 -122
  123. package/tests/workspaceEvents.test.ts +0 -221
  124. package/tsconfig.json +0 -10
@@ -0,0 +1,110 @@
1
+ import { normalisePathname } from './normalise';
2
+ import { stripLanguagePrefix } from './pathname';
3
+
4
+ export type LocaleRedirectSkipRule =
5
+ | {
6
+ type: 'exact';
7
+ path: string;
8
+ }
9
+ | {
10
+ type: 'prefix';
11
+ path: string;
12
+ }
13
+ | {
14
+ type: 'pattern';
15
+ path: RegExp;
16
+ };
17
+
18
+ /**
19
+ * Federation artifacts are never pages. The remote entries match the same
20
+ * shapes the federation layer itself recognises and serves (`mfCache.ts`):
21
+ * a manifest may name a root entry such as `remoteEntry.catalog.js`.
22
+ */
23
+ export const DEFAULT_LOCALE_REDIRECT_SKIP_RULES: readonly LocaleRedirectSkipRule[] =
24
+ [
25
+ { type: 'exact', path: '/backend-mf-manifest.json' },
26
+ { type: 'exact', path: '/mf-manifest.json' },
27
+ { type: 'exact', path: '/mf-stats.json' },
28
+ {
29
+ type: 'pattern',
30
+ path: /^\/(?:backendRemoteEntry(?:\.[a-zA-Z0-9_-]+)?\.cjs|remoteEntry(?:\.[a-zA-Z0-9_-]+)?\.js)$/,
31
+ },
32
+ // The static server admits only the container assets named by its
33
+ // manifest; locale middleware must not redirect their Node chunk URLs.
34
+ { type: 'pattern', path: /^\/bundles\/(?:[^/]+\/)*[^/]+\.(?:js|cjs|mjs)$/ },
35
+ { type: 'prefix', path: '/static/' },
36
+ { type: 'prefix', path: '/upload/' },
37
+ ];
38
+
39
+ const stripTrailingSlash = (pathname: string): string =>
40
+ pathname.length > 1 ? pathname.replace(/\/+$/, '') : pathname;
41
+
42
+ export const matchesPathPrefix = (
43
+ pathname: string,
44
+ prefix: string,
45
+ ): boolean => {
46
+ const normalizedPathname = normalisePathname(pathname);
47
+ const normalizedPrefix = stripTrailingSlash(normalisePathname(prefix));
48
+
49
+ return (
50
+ normalizedPathname === normalizedPrefix ||
51
+ normalizedPathname.startsWith(`${normalizedPrefix}/`)
52
+ );
53
+ };
54
+
55
+ const matchesSkipRule = (
56
+ pathname: string,
57
+ rule: LocaleRedirectSkipRule,
58
+ ): boolean => {
59
+ if (rule.type === 'exact') {
60
+ return normalisePathname(pathname) === normalisePathname(rule.path);
61
+ }
62
+ if (rule.type === 'pattern') {
63
+ return rule.path.test(normalisePathname(pathname));
64
+ }
65
+
66
+ return matchesPathPrefix(pathname, rule.path);
67
+ };
68
+
69
+ export const isDefaultLocaleRedirectSkipPath = (
70
+ pathname: string,
71
+ languages: string[] = [],
72
+ ): boolean => {
73
+ const normalizedPathname = normalisePathname(pathname);
74
+ const pathWithoutLanguage = stripLanguagePrefix(
75
+ normalizedPathname,
76
+ languages,
77
+ );
78
+
79
+ return DEFAULT_LOCALE_REDIRECT_SKIP_RULES.some(rule =>
80
+ matchesSkipRule(pathWithoutLanguage, rule),
81
+ );
82
+ };
83
+
84
+ export const shouldSkipLocaleRedirect = (
85
+ pathname: string,
86
+ languages: string[] = [],
87
+ ignoreRedirectRoutes?: string[] | ((pathname: string) => boolean),
88
+ ): boolean => {
89
+ const normalizedPathname = normalisePathname(pathname);
90
+ const pathWithoutLanguage = stripLanguagePrefix(
91
+ normalizedPathname,
92
+ languages,
93
+ );
94
+
95
+ if (isDefaultLocaleRedirectSkipPath(pathWithoutLanguage)) {
96
+ return true;
97
+ }
98
+
99
+ if (!ignoreRedirectRoutes) {
100
+ return false;
101
+ }
102
+
103
+ if (typeof ignoreRedirectRoutes === 'function') {
104
+ return ignoreRedirectRoutes(pathWithoutLanguage);
105
+ }
106
+
107
+ return ignoreRedirectRoutes.some(pattern =>
108
+ matchesPathPrefix(pathWithoutLanguage, pattern),
109
+ );
110
+ };
@@ -0,0 +1,106 @@
1
+ import { normalisePathname } from './normalise';
2
+
3
+ import {
4
+ buildPathFromPattern,
5
+ matchPathPattern,
6
+ sortPatternsBySpecificity,
7
+ } from './patterns';
8
+
9
+ import type { LocalisedUrlsMap } from './types';
10
+
11
+ type CanonicalCandidate = {
12
+ pattern: string;
13
+ canonicalPattern: string;
14
+ localisedUrlEntry: LocalisedUrlsMap[string];
15
+ };
16
+
17
+ type ResolveLocalisedUrlPathOptions =
18
+ | {
19
+ mode: 'localised';
20
+ targetLanguage: string;
21
+ }
22
+ | {
23
+ mode: 'canonical';
24
+ };
25
+
26
+ const getCanonicalCandidates = (
27
+ localisedUrls: LocalisedUrlsMap,
28
+ ): CanonicalCandidate[] =>
29
+ sortPatternsBySpecificity(
30
+ Object.entries(localisedUrls).map(
31
+ ([canonicalPattern, localisedUrlEntry]) => ({
32
+ pattern: canonicalPattern,
33
+ canonicalPattern,
34
+ localisedUrlEntry,
35
+ }),
36
+ ),
37
+ );
38
+
39
+ const resolveLocalisedUrlPath = (
40
+ pathname: string,
41
+ languages: string[],
42
+ localisedUrls: LocalisedUrlsMap,
43
+ options: ResolveLocalisedUrlPathOptions,
44
+ ): string => {
45
+ const normalizedPathname = normalisePathname(pathname);
46
+ const canonicalCandidates = getCanonicalCandidates(localisedUrls);
47
+
48
+ const candidates = sortPatternsBySpecificity(
49
+ canonicalCandidates.flatMap(({ canonicalPattern, localisedUrlEntry }) => {
50
+ const targetPattern =
51
+ options.mode === 'localised'
52
+ ? localisedUrlEntry[options.targetLanguage]
53
+ : canonicalPattern;
54
+ if (!targetPattern) {
55
+ return [];
56
+ }
57
+
58
+ const sourcePatterns = new Set([
59
+ canonicalPattern,
60
+ ...languages
61
+ .map(language => localisedUrlEntry[language])
62
+ .filter((sourcePattern): sourcePattern is string =>
63
+ Boolean(sourcePattern),
64
+ ),
65
+ ]);
66
+ return Array.from(sourcePatterns, sourcePattern => ({
67
+ pattern: sourcePattern,
68
+ sourcePattern,
69
+ targetPattern,
70
+ }));
71
+ }),
72
+ );
73
+
74
+ for (const { sourcePattern, targetPattern } of candidates) {
75
+ const params = matchPathPattern(normalizedPathname, sourcePattern);
76
+ if (params) {
77
+ return buildPathFromPattern(targetPattern, params);
78
+ }
79
+ }
80
+
81
+ return normalizedPathname;
82
+ };
83
+
84
+ export const resolveLocalisedPath = (
85
+ pathname: string,
86
+ targetLanguage: string,
87
+ languages: string[],
88
+ localisedUrls: LocalisedUrlsMap,
89
+ ): string =>
90
+ resolveLocalisedUrlPath(pathname, languages, localisedUrls, {
91
+ mode: 'localised',
92
+ targetLanguage,
93
+ });
94
+
95
+ /**
96
+ * Reverse-map language-specific pathnames back to language-agnostic
97
+ * LocalisedUrlsMap keys.
98
+ */
99
+ export const resolveCanonicalLocalisedPath = (
100
+ pathname: string,
101
+ languages: string[],
102
+ localisedUrls: LocalisedUrlsMap,
103
+ ): string =>
104
+ resolveLocalisedUrlPath(pathname, languages, localisedUrls, {
105
+ mode: 'canonical',
106
+ });
@@ -0,0 +1,83 @@
1
+ import { normalisePathPattern } from './normalise';
2
+ import { buildPathFromPattern, matchPathPattern } from './patterns';
3
+
4
+ interface RouteIdentity {
5
+ id?: string;
6
+ path?: string;
7
+ canonicalPath: string;
8
+ paths?: Record<string, string>;
9
+ }
10
+
11
+ interface RouteNode {
12
+ id?: string;
13
+ path?: string;
14
+ children?: RouteNode[];
15
+ modernLocalisedRoute?: RouteIdentity;
16
+ }
17
+
18
+ type PathRewrite = { from: string; to: string };
19
+
20
+ function rewritePath(url: URL, mappings: PathRewrite[]) {
21
+ for (const { from, to } of mappings) {
22
+ const params = matchPathPattern(url.pathname, from);
23
+ if (params) {
24
+ url.pathname = buildPathFromPattern(to, params);
25
+ break;
26
+ }
27
+ }
28
+ return url;
29
+ }
30
+
31
+ /** Use the router's URL rewrite seam; browser URLs retain their locale spelling. */
32
+ export function createLocalisedRouteRewrite(routes: RouteNode[]) {
33
+ const input: PathRewrite[] = [];
34
+ const output: PathRewrite[] = [];
35
+ const visit = (nodes: RouteNode[], parent: string) => {
36
+ for (const route of nodes) {
37
+ const fullPath = normalisePathPattern(`${parent}/${route.path ?? ''}`);
38
+ const identity = route.modernLocalisedRoute;
39
+ if (identity?.paths && fullPath.endsWith(identity.canonicalPath)) {
40
+ const prefix = fullPath.slice(0, -identity.canonicalPath.length);
41
+ const hasLocale = prefix
42
+ .split('/')
43
+ .some(segment => /^:(?:lang|locale|language)\??$/.test(segment));
44
+ for (const [language, localisedPath] of Object.entries(
45
+ identity.paths,
46
+ )) {
47
+ const localePrefix = prefix
48
+ .split('/')
49
+ .map(segment =>
50
+ /^:(?:lang|locale|language)\??$/.test(segment)
51
+ ? language
52
+ : segment,
53
+ )
54
+ .join('/');
55
+ const canonical = normalisePathPattern(
56
+ `${localePrefix}/${identity.canonicalPath}`,
57
+ );
58
+ const localised = normalisePathPattern(
59
+ `${localePrefix}/${localisedPath}`,
60
+ );
61
+ if (canonical !== localised) {
62
+ input.push({ from: localised, to: canonical });
63
+ if (hasLocale) output.push({ from: canonical, to: localised });
64
+ }
65
+ }
66
+ }
67
+ if (route.children) visit(route.children, fullPath);
68
+ }
69
+ };
70
+ visit(routes, '');
71
+ if (input.length === 0) return undefined;
72
+ const specificity = (pattern: string) =>
73
+ pattern
74
+ .split('/')
75
+ .filter(segment => segment && !segment.startsWith(':') && segment !== '*')
76
+ .length;
77
+ for (const mappings of [input, output])
78
+ mappings.sort((a, b) => specificity(b.from) - specificity(a.from));
79
+ return {
80
+ input: ({ url }: { url: URL }) => rewritePath(url, input),
81
+ output: ({ url }: { url: URL }) => rewritePath(url, output),
82
+ };
83
+ }