@bleedingdev/modern-js-runtime-extensions 3.9.0-ultramodern.8 → 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 (117) 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/esm/localisedUrls/config.mjs +11 -0
  17. package/dist/esm/localisedUrls/index.mjs +8 -0
  18. package/dist/esm/localisedUrls/normalise.mjs +37 -0
  19. package/dist/esm/localisedUrls/pathname.mjs +24 -0
  20. package/dist/esm/localisedUrls/patterns.mjs +152 -0
  21. package/dist/esm/localisedUrls/redirect.mjs +57 -0
  22. package/dist/esm/localisedUrls/resolve.mjs +37 -0
  23. package/dist/esm/localisedUrls/routeIdentity.mjs +54 -0
  24. package/dist/esm/localisedUrls/routes.mjs +176 -0
  25. package/dist/esm/localisedUrls/types.mjs +0 -0
  26. package/dist/esm/rendererHead.mjs +14 -5
  27. package/dist/esm/request-policy/policyCore.mjs +1 -1
  28. package/dist/esm/request-policy/transport.mjs +2 -2
  29. package/dist/esm/routerState.mjs +81 -73
  30. package/dist/esm/routerStatePlugin.mjs +62 -11
  31. package/dist/esm-node/localisedUrls/config.mjs +12 -0
  32. package/dist/esm-node/localisedUrls/index.mjs +9 -0
  33. package/dist/esm-node/localisedUrls/normalise.mjs +38 -0
  34. package/dist/esm-node/localisedUrls/pathname.mjs +25 -0
  35. package/dist/esm-node/localisedUrls/patterns.mjs +153 -0
  36. package/dist/esm-node/localisedUrls/redirect.mjs +58 -0
  37. package/dist/esm-node/localisedUrls/resolve.mjs +38 -0
  38. package/dist/esm-node/localisedUrls/routeIdentity.mjs +55 -0
  39. package/dist/esm-node/localisedUrls/routes.mjs +177 -0
  40. package/dist/esm-node/localisedUrls/types.mjs +1 -0
  41. package/dist/esm-node/rendererHead.mjs +14 -5
  42. package/dist/esm-node/request-policy/policyCore.mjs +1 -1
  43. package/dist/esm-node/request-policy/transport.mjs +2 -2
  44. package/dist/esm-node/routerState.mjs +81 -73
  45. package/dist/esm-node/routerStatePlugin.mjs +62 -11
  46. package/dist/types/helmetCompat.d.ts +1 -1
  47. package/dist/types/localisedUrls/config.d.ts +9 -0
  48. package/dist/types/localisedUrls/index.d.ts +10 -0
  49. package/dist/types/localisedUrls/normalise.d.ts +10 -0
  50. package/dist/types/localisedUrls/pathname.d.ts +4 -0
  51. package/dist/types/localisedUrls/patterns.d.ts +11 -0
  52. package/dist/types/localisedUrls/redirect.d.ts +19 -0
  53. package/dist/types/localisedUrls/resolve.d.ts +7 -0
  54. package/dist/types/localisedUrls/routeIdentity.d.ts +22 -0
  55. package/dist/types/localisedUrls/routes.d.ts +3 -0
  56. package/dist/types/localisedUrls/types.d.ts +14 -0
  57. package/dist/types/rendererHead.d.ts +5 -3
  58. package/dist/types/request-policy/types.d.ts +1 -1
  59. package/dist/types/routeHydration.d.ts +2 -2
  60. package/dist/types/routerProvider.d.ts +1 -3
  61. package/dist/types/routerState.d.ts +11 -6
  62. package/dist/types/routerStatePlugin.d.ts +12 -0
  63. package/dist/types/routerStateTypes.d.ts +47 -17
  64. package/package.json +42 -44
  65. package/src/helmetCompat.ts +1 -1
  66. package/src/localisedUrls/config.ts +18 -0
  67. package/src/localisedUrls/index.ts +25 -0
  68. package/src/localisedUrls/normalise.ts +62 -0
  69. package/src/localisedUrls/pathname.ts +60 -0
  70. package/src/localisedUrls/patterns.ts +283 -0
  71. package/src/localisedUrls/redirect.ts +110 -0
  72. package/src/localisedUrls/resolve.ts +106 -0
  73. package/src/localisedUrls/routeIdentity.ts +83 -0
  74. package/src/localisedUrls/routes.ts +451 -0
  75. package/src/localisedUrls/types.ts +18 -0
  76. package/src/rendererHead.ts +25 -8
  77. package/src/request-policy/policyCore.ts +1 -1
  78. package/src/request-policy/transport.ts +2 -2
  79. package/src/request-policy/types.ts +1 -1
  80. package/src/routeHydration.ts +2 -2
  81. package/src/routerProvider.ts +1 -1
  82. package/src/routerState.ts +127 -115
  83. package/src/routerStatePlugin.ts +106 -10
  84. package/src/routerStateTypes.ts +47 -17
  85. package/dist/cjs/buildIdentity.js +0 -73
  86. package/dist/esm/buildIdentity.mjs +0 -35
  87. package/dist/esm/reactContext.mjs +0 -7
  88. package/dist/esm-node/buildIdentity.mjs +0 -36
  89. package/dist/esm-node/reactContext.mjs +0 -8
  90. package/dist/types/buildIdentity.d.ts +0 -37
  91. package/dist/types/reactContext.d.ts +0 -6
  92. package/rslib.config.mts +0 -19
  93. package/rstest.config.mts +0 -38
  94. package/src/buildIdentity.ts +0 -61
  95. package/src/reactContext.ts +0 -16
  96. package/tests/buildIdentity.test.ts +0 -51
  97. package/tests/contextExtensions.test.ts +0 -27
  98. package/tests/headRuntime.test.tsx +0 -229
  99. package/tests/rendererHead.test.ts +0 -187
  100. package/tests/request-policy/browser-policy.test.ts +0 -108
  101. package/tests/request-policy/client-isolation.test.ts +0 -109
  102. package/tests/request-policy/node-policy.test.ts +0 -447
  103. package/tests/request-policy/policyCore.test.ts +0 -129
  104. package/tests/request-policy/requestContext.test.ts +0 -66
  105. package/tests/request-policy/requestFactory.outbound-contract.test.ts +0 -290
  106. package/tests/request-policy/traceparent.test.ts +0 -52
  107. package/tests/request-policy/transport-retry.test.ts +0 -367
  108. package/tests/routeHydration.test.ts +0 -180
  109. package/tests/routerPrefetchPolicy.test.ts +0 -102
  110. package/tests/routerProvider.test.ts +0 -29
  111. package/tests/routerState.test.ts +0 -78
  112. package/tests/routerStatePlugin.test.ts +0 -63
  113. package/tests/runtimeContextProjection.test.ts +0 -83
  114. package/tests/safeFailure.test.ts +0 -136
  115. package/tests/shellStream.test.ts +0 -122
  116. package/tests/workspaceEvents.test.ts +0 -221
  117. package/tsconfig.json +0 -10
@@ -8,99 +8,107 @@ function getRouterRuntimeState(runtimeContext) {
8
8
  function getRouterServerSnapshot(runtimeContext) {
9
9
  return routerServerSnapshotExtension.get(runtimeContext);
10
10
  }
11
- function toHydrationScripts(state) {
12
- if (state.hydrationScripts?.length) return state.hydrationScripts;
13
- return state.hydrationScript ? [
14
- state.hydrationScript
15
- ] : void 0;
16
- }
17
- function getMatchedRouteIdsFromMatches(matches) {
18
- const routeIds = matches?.map((match)=>match.assetRouteId ?? match.routeId).filter((routeId)=>'string' == typeof routeId);
19
- return routeIds?.length ? routeIds : void 0;
20
- }
21
- function createRouterServerSnapshot(state) {
22
- const hydrationScripts = toHydrationScripts(state);
23
- const matchedRouteIds = state.matchedRouteIds ?? getMatchedRouteIdsFromMatches(state.matches);
24
- return {
25
- ...state,
26
- ...hydrationScripts?.length ? {
27
- hydrationScript: state.hydrationScript ?? hydrationScripts[0],
28
- hydrationScripts
11
+ function createRouterServerSnapshot(snapshot) {
12
+ const matches = snapshot.matches?.map((match)=>Object.freeze({
13
+ ...match,
14
+ ...match.params ? {
15
+ params: Object.freeze({
16
+ ...match.params
17
+ })
18
+ } : {}
19
+ }));
20
+ const matchedRouteIds = snapshot.matchedRouteIds ?? matches?.map((match)=>match.assetRouteId ?? match.routeId);
21
+ return Object.freeze({
22
+ ...snapshot,
23
+ ...snapshot.hydrationScripts ? {
24
+ hydrationScripts: Object.freeze([
25
+ ...snapshot.hydrationScripts
26
+ ])
29
27
  } : {},
30
28
  ...matchedRouteIds ? {
31
- matchedRouteIds
32
- } : {}
33
- };
34
- }
35
- function createRouterRuntimeState(state) {
36
- const hasSnapshotState = Boolean(state.serverSnapshot) || Boolean(state.hydrationScript) || Boolean(state.hydrationScripts?.length) || Boolean(state.matchedRouteIds?.length) || Boolean(state.matches?.length);
37
- const serverSnapshot = state.serverSnapshot ? createRouterServerSnapshot({
38
- ...state.serverSnapshot,
39
- framework: state.serverSnapshot.framework ?? state.framework,
40
- basename: state.serverSnapshot.basename ?? state.basename,
41
- hydrationScript: state.serverSnapshot.hydrationScript ?? state.hydrationScript,
42
- hydrationScripts: state.serverSnapshot.hydrationScripts ?? state.hydrationScripts,
43
- matchedRouteIds: state.serverSnapshot.matchedRouteIds ?? state.matchedRouteIds,
44
- matches: state.serverSnapshot.matches ?? state.matches
45
- }) : hasSnapshotState ? createRouterServerSnapshot({
46
- framework: state.framework,
47
- basename: state.basename,
48
- hydrationScript: state.hydrationScript,
49
- hydrationScripts: state.hydrationScripts,
50
- matchedRouteIds: state.matchedRouteIds,
51
- matches: state.matches
52
- }) : void 0;
53
- const hydrationScripts = toHydrationScripts({
54
- hydrationScript: state.hydrationScript ?? serverSnapshot?.hydrationScript,
55
- hydrationScripts: state.hydrationScripts ?? serverSnapshot?.hydrationScripts
56
- });
57
- const matchedRouteIds = state.matchedRouteIds ?? serverSnapshot?.matchedRouteIds ?? getMatchedRouteIdsFromMatches(state.matches);
58
- return {
59
- ...state,
60
- ...hydrationScripts?.length ? {
61
- hydrationScript: state.hydrationScript ?? hydrationScripts[0],
62
- hydrationScripts
29
+ matchedRouteIds: Object.freeze([
30
+ ...matchedRouteIds
31
+ ])
63
32
  } : {},
64
- ...matchedRouteIds ? {
65
- matchedRouteIds
33
+ ...matches ? {
34
+ matches: Object.freeze(matches)
66
35
  } : {},
67
- ...serverSnapshot ? {
68
- serverSnapshot
36
+ ...snapshot.errors ? {
37
+ errors: Object.freeze({
38
+ ...snapshot.errors
39
+ })
40
+ } : {},
41
+ ...snapshot.routerData ? {
42
+ routerData: Object.freeze({
43
+ ...snapshot.routerData,
44
+ ...snapshot.routerData.loaderData ? {
45
+ loaderData: Object.freeze({
46
+ ...snapshot.routerData.loaderData
47
+ })
48
+ } : {},
49
+ ...snapshot.routerData.errors ? {
50
+ errors: Object.freeze({
51
+ ...snapshot.routerData.errors
52
+ })
53
+ } : {}
54
+ })
69
55
  } : {}
56
+ });
57
+ }
58
+ const routerRuntimeStateListeners = new WeakMap();
59
+ function subscribeRouterRuntimeState(runtimeContext, listener) {
60
+ if (!runtimeContext || 'object' != typeof runtimeContext) return ()=>void 0;
61
+ let listeners = routerRuntimeStateListeners.get(runtimeContext);
62
+ if (!listeners) {
63
+ listeners = new Set();
64
+ routerRuntimeStateListeners.set(runtimeContext, listeners);
65
+ }
66
+ listeners.add(listener);
67
+ return ()=>{
68
+ listeners?.delete(listener);
70
69
  };
71
70
  }
71
+ const pendingRouterRuntimeStateNotifications = new WeakSet();
72
+ function notifyRouterRuntimeState(runtimeContext) {
73
+ const listeners = routerRuntimeStateListeners.get(runtimeContext);
74
+ if (!listeners?.size) return;
75
+ if (pendingRouterRuntimeStateNotifications.has(runtimeContext)) return;
76
+ pendingRouterRuntimeStateNotifications.add(runtimeContext);
77
+ const deliver = ()=>{
78
+ pendingRouterRuntimeStateNotifications.delete(runtimeContext);
79
+ const current = routerRuntimeStateListeners.get(runtimeContext);
80
+ if (!current?.size) return;
81
+ for (const listener of [
82
+ ...current
83
+ ])try {
84
+ listener();
85
+ } catch {}
86
+ };
87
+ if ('function' == typeof queueMicrotask) queueMicrotask(deliver);
88
+ else setTimeout(deliver, 0);
89
+ }
72
90
  function applyRouterRuntimeState(runtimeContext, state) {
73
- const normalized = createRouterRuntimeState(state);
74
- routerRuntimeStateExtension.set(runtimeContext, normalized);
75
- if (normalized.serverSnapshot) routerServerSnapshotExtension.set(runtimeContext, normalized.serverSnapshot);
91
+ const previous = routerRuntimeStateExtension.get(runtimeContext);
92
+ routerRuntimeStateExtension.set(runtimeContext, state);
93
+ if (previous?.instance !== state.instance || previous?.framework !== state.framework || previous?.navigation !== state.navigation) notifyRouterRuntimeState(runtimeContext);
76
94
  return runtimeContext;
77
95
  }
78
96
  function applyRouterServerPrepareResult(runtimeContext, result) {
79
- const state = createRouterRuntimeState({
97
+ if (result.snapshot) routerServerSnapshotExtension.set(runtimeContext, createRouterServerSnapshot(result.snapshot));
98
+ return applyRouterRuntimeState(runtimeContext, {
80
99
  ...result.state,
81
- cleanup: result.cleanup ?? result.state.cleanup,
82
- serverSnapshot: result.snapshot ?? result.state.serverSnapshot
100
+ cleanup: result.cleanup ?? result.state.cleanup
83
101
  });
84
- applyRouterRuntimeState(runtimeContext, state);
85
- return runtimeContext;
86
102
  }
87
103
  function getRouterHydrationScripts(runtimeContext) {
88
- const serverSnapshot = getRouterServerSnapshot(runtimeContext);
89
- const runtimeState = getRouterRuntimeState(runtimeContext);
90
- return serverSnapshot?.hydrationScripts ?? toHydrationScripts({
91
- hydrationScript: serverSnapshot?.hydrationScript
92
- }) ?? runtimeState?.hydrationScripts ?? toHydrationScripts({
93
- hydrationScript: runtimeState?.hydrationScript
94
- }) ?? [];
104
+ return getRouterServerSnapshot(runtimeContext)?.hydrationScripts ?? [];
95
105
  }
96
106
  function getRouterMatchedRouteIds(runtimeContext) {
97
- const serverSnapshot = getRouterServerSnapshot(runtimeContext);
98
- const runtimeState = getRouterRuntimeState(runtimeContext);
99
- return serverSnapshot?.matchedRouteIds ?? getMatchedRouteIdsFromMatches(serverSnapshot?.matches) ?? runtimeState?.matchedRouteIds ?? getMatchedRouteIdsFromMatches(runtimeState?.matches);
107
+ return getRouterServerSnapshot(runtimeContext)?.matchedRouteIds;
100
108
  }
101
109
  async function cleanupRouterRuntimeState(runtimeContext) {
102
110
  try {
103
111
  await getRouterRuntimeState(runtimeContext)?.cleanup?.();
104
112
  } catch {}
105
113
  }
106
- export { applyRouterRuntimeState, applyRouterServerPrepareResult, cleanupRouterRuntimeState, createRouterRuntimeState, createRouterServerSnapshot, getRouterHydrationScripts, getRouterMatchedRouteIds, getRouterRuntimeState, getRouterServerSnapshot };
114
+ export { applyRouterRuntimeState, applyRouterServerPrepareResult, cleanupRouterRuntimeState, createRouterServerSnapshot, getRouterHydrationScripts, getRouterMatchedRouteIds, getRouterRuntimeState, getRouterServerSnapshot, subscribeRouterRuntimeState };
@@ -1,21 +1,64 @@
1
1
  import "node:module";
2
2
  import { createRouterPrefetchPolicy } from "./routerPrefetchPolicy.mjs";
3
- import { applyRouterRuntimeState, createRouterServerSnapshot } from "./routerState.mjs";
3
+ import { applyRouterRuntimeState, applyRouterServerPrepareResult, getRouterRuntimeState, getRouterServerSnapshot } from "./routerState.mjs";
4
+ const ROUTER_CLEANUP_ERROR = 'An error occurs during router runtime cleanup';
5
+ const installedRouterPolicies = new WeakSet();
6
+ function createReactRouterNavigation(router, Link) {
7
+ let state;
8
+ let snapshot;
9
+ return {
10
+ Link,
11
+ getSnapshot () {
12
+ if (state !== router.state) {
13
+ state = router.state;
14
+ snapshot = {
15
+ location: state.location,
16
+ params: Object.assign({}, ...state.matches.map((match)=>match.params))
17
+ };
18
+ }
19
+ return snapshot;
20
+ },
21
+ subscribe: (listener)=>router.subscribe(listener),
22
+ navigate: (href, options)=>router.navigate(href, options)
23
+ };
24
+ }
4
25
  function createRouterStatePlugin({ registryHooks }) {
5
26
  return {
6
27
  name: '@modern-js/router-runtime-policy',
7
28
  registryHooks,
8
29
  setup (api) {
30
+ if (installedRouterPolicies.has(api)) return;
31
+ installedRouterPolicies.add(api);
9
32
  api.onBeforeRender((context)=>{
10
33
  const runtimeContext = context;
11
34
  runtimeContext.linkPrefetchPolicy ??= createRouterPrefetchPolicy();
12
35
  });
36
+ api.onRenderPrepared((info)=>{
37
+ const snapshot = getRouterServerSnapshot(info.runtimeContext);
38
+ if (!snapshot) return info;
39
+ return {
40
+ ...info,
41
+ routerResult: {
42
+ statusCode: snapshot.statusCode ?? info.routerResult?.statusCode,
43
+ errors: snapshot.errors ?? info.routerResult?.errors
44
+ }
45
+ };
46
+ });
47
+ api.onRequestEnd(async ({ runtimeContext })=>{
48
+ try {
49
+ await getRouterRuntimeState(runtimeContext)?.cleanup?.();
50
+ } catch (error) {
51
+ const { ssrContext } = runtimeContext;
52
+ if (!ssrContext) throw error;
53
+ ssrContext.onError(error, ROUTER_CLEANUP_ERROR);
54
+ }
55
+ });
13
56
  api.onAfterCreateRouter((event)=>{
14
57
  if ('react-router' !== event.framework) return;
15
58
  if ('ssr-prepare' === event.phase) {
16
59
  const { routerContext } = event.runtimeContext;
17
60
  if (!routerContext) return;
18
- const serverSnapshot = createRouterServerSnapshot({
61
+ const snapshot = {
19
62
  framework: event.framework,
20
63
  basename: event.basename,
21
64
  statusCode: routerContext.statusCode,
@@ -29,21 +72,29 @@ function createRouterStatePlugin({ registryHooks }) {
29
72
  routeId: match.route.id
30
73
  }
31
74
  ] : [])
75
+ };
76
+ applyRouterServerPrepareResult(event.runtimeContext, {
77
+ snapshot,
78
+ state: {
79
+ framework: event.framework,
80
+ basename: event.basename,
81
+ instance: event.router
82
+ }
32
83
  });
84
+ } else if ('client-create' === event.phase) {
85
+ const router = event.router;
86
+ const { router: api } = event.runtimeContext;
33
87
  applyRouterRuntimeState(event.runtimeContext, {
34
88
  framework: event.framework,
35
89
  basename: event.basename,
36
- instance: event.router,
37
- matchedRouteIds: serverSnapshot.matchedRouteIds,
38
- serverSnapshot
90
+ instance: router,
91
+ ...api?.Link ? {
92
+ navigation: createReactRouterNavigation(router, api.Link)
93
+ } : {}
39
94
  });
40
- } else if ('client-create' === event.phase) applyRouterRuntimeState(event.runtimeContext, {
41
- framework: event.framework,
42
- basename: event.basename,
43
- instance: event.router
44
- });
95
+ }
45
96
  });
46
97
  }
47
98
  };
48
99
  }
49
- export { createRouterStatePlugin };
100
+ export { ROUTER_CLEANUP_ERROR, createRouterStatePlugin };
@@ -63,7 +63,7 @@ export type HelmetCompatState<Element> = {
63
63
  style: HeadDatum<Element>;
64
64
  title: HeadDatum<Element>;
65
65
  };
66
- export declare const renderHeadMarker: <Element>(react: ElementFactory<Element>, marker: MarkerProps | null | undefined) => Element | null;
66
+ export declare const renderHeadMarker: <Element>(react: ElementFactory<Element>, marker: MarkerProps | undefined) => Element | null;
67
67
  export declare const markPriorityHeadRecord: (record: object, prioritize: boolean | undefined) => void;
68
68
  export declare const isPriorityHeadTag: (tag: object) => boolean;
69
69
  export declare const headTagsToComponents: <Element>(react: ElementFactory<Element>, tagName: string, tags: HeadTag[]) => Element[];
@@ -0,0 +1,9 @@
1
+ import type { LocalisedUrlsOption, ResolvedLocalisedUrlsConfig } from './types.js';
2
+ /**
3
+ * Localised URLs are strictly opt-in: only an explicit, non-empty map enables
4
+ * route expansion and validation. `true`, `false`, an empty map and absence
5
+ * all resolve to disabled, so upstream-style configs (`localePathRedirect` +
6
+ * `languages` without a map) keep plain locale-prefix behavior instead of
7
+ * failing the build for every route missing from a map they never wrote.
8
+ */
9
+ export declare const resolveLocalisedUrlsConfig: (option: LocalisedUrlsOption | undefined) => ResolvedLocalisedUrlsConfig;
@@ -0,0 +1,10 @@
1
+ export { resolveLocalisedUrlsConfig } from './config.js';
2
+ export { normalisePathname, normalisePathPattern } from './normalise.js';
3
+ export { canonicalTargetPathname, localiseTargetPathname, stripLanguagePrefix, } from './pathname.js';
4
+ export { buildPathFromPattern, matchPathPattern } from './patterns.js';
5
+ export type { LocaleRedirectSkipRule } from './redirect.js';
6
+ export { DEFAULT_LOCALE_REDIRECT_SKIP_RULES, isDefaultLocaleRedirectSkipPath, matchesPathPrefix, shouldSkipLocaleRedirect, } from './redirect.js';
7
+ export { resolveCanonicalLocalisedPath, resolveLocalisedPath } from './resolve.js';
8
+ export { createLocalisedRouteRewrite } from './routeIdentity.js';
9
+ export { applyLocalisedUrlsToRoutes, validateLocalisedUrls } from './routes.js';
10
+ export type { LocalisedRoute, LocalisedUrlPathMap, LocalisedUrlsMap, LocalisedUrlsOption, ResolvedLocalisedUrlsConfig, } from './types.js';
@@ -0,0 +1,10 @@
1
+ export declare const normalisePathPattern: (path: string) => string;
2
+ /**
3
+ * Normalise a concrete request pathname: slash cleanup only. Unlike
4
+ * {@link normalisePathPattern} it must not rewrite literal `[x]` segments to
5
+ * `:x` params — pathnames are values, not patterns.
6
+ */
7
+ export declare const normalisePathname: (pathname: string) => string;
8
+ export declare const normaliseRoutePath: (path: string) => string;
9
+ export declare const stripLeadingLocaleParam: (path?: string) => string | undefined;
10
+ export declare const getLeadingLocaleParam: (path?: string) => string | null;
@@ -0,0 +1,4 @@
1
+ import type { LocalisedUrlsOption } from './types.js';
2
+ export declare const stripLanguagePrefix: (pathname: string, languages: string[]) => string;
3
+ export declare const localiseTargetPathname: (pathname: string, language: string, languages: string[], localisedUrls?: LocalisedUrlsOption) => string;
4
+ export declare const canonicalTargetPathname: (pathname: string, languages: string[], localisedUrls?: LocalisedUrlsOption) => string;
@@ -0,0 +1,11 @@
1
+ export declare const getOrderedPathParamSignature: (pattern: string) => string[];
2
+ export declare const getPathParamSignature: (pattern: string) => string[];
3
+ export declare const getPathPatternValidationIssue: (pattern: string) => string | undefined;
4
+ export declare const getPhysicalPathPattern: (pattern: string) => string;
5
+ export declare const haveEqualPatternSpecificity: (left: string, right: string) => boolean;
6
+ export declare const doPathPatternsOverlap: (left: string, right: string) => boolean;
7
+ export declare const sortPatternsBySpecificity: <T extends {
8
+ pattern: string;
9
+ }>(patterns: T[]) => T[];
10
+ export declare const matchPathPattern: (pathname: string, pattern: string) => Record<string, string> | null;
11
+ export declare const buildPathFromPattern: (pattern: string, params: Record<string, string>) => string;
@@ -0,0 +1,19 @@
1
+ export type LocaleRedirectSkipRule = {
2
+ type: 'exact';
3
+ path: string;
4
+ } | {
5
+ type: 'prefix';
6
+ path: string;
7
+ } | {
8
+ type: 'pattern';
9
+ path: RegExp;
10
+ };
11
+ /**
12
+ * Federation artifacts are never pages. The remote entries match the same
13
+ * shapes the federation layer itself recognises and serves (`mfCache.ts`):
14
+ * a manifest may name a root entry such as `remoteEntry.catalog.js`.
15
+ */
16
+ export declare const DEFAULT_LOCALE_REDIRECT_SKIP_RULES: readonly LocaleRedirectSkipRule[];
17
+ export declare const matchesPathPrefix: (pathname: string, prefix: string) => boolean;
18
+ export declare const isDefaultLocaleRedirectSkipPath: (pathname: string, languages?: string[]) => boolean;
19
+ export declare const shouldSkipLocaleRedirect: (pathname: string, languages?: string[], ignoreRedirectRoutes?: string[] | ((pathname: string) => boolean)) => boolean;
@@ -0,0 +1,7 @@
1
+ import type { LocalisedUrlsMap } from './types.js';
2
+ export declare const resolveLocalisedPath: (pathname: string, targetLanguage: string, languages: string[], localisedUrls: LocalisedUrlsMap) => string;
3
+ /**
4
+ * Reverse-map language-specific pathnames back to language-agnostic
5
+ * LocalisedUrlsMap keys.
6
+ */
7
+ export declare const resolveCanonicalLocalisedPath: (pathname: string, languages: string[], localisedUrls: LocalisedUrlsMap) => string;
@@ -0,0 +1,22 @@
1
+ interface RouteIdentity {
2
+ id?: string;
3
+ path?: string;
4
+ canonicalPath: string;
5
+ paths?: Record<string, string>;
6
+ }
7
+ interface RouteNode {
8
+ id?: string;
9
+ path?: string;
10
+ children?: RouteNode[];
11
+ modernLocalisedRoute?: RouteIdentity;
12
+ }
13
+ /** Use the router's URL rewrite seam; browser URLs retain their locale spelling. */
14
+ export declare function createLocalisedRouteRewrite(routes: RouteNode[]): {
15
+ input: ({ url }: {
16
+ url: URL;
17
+ }) => URL;
18
+ output: ({ url }: {
19
+ url: URL;
20
+ }) => URL;
21
+ } | undefined;
22
+ export {};
@@ -0,0 +1,3 @@
1
+ import type { LocalisedRoute, LocalisedUrlsMap } from './types.js';
2
+ export declare const validateLocalisedUrls: (routes: LocalisedRoute[], languages: string[], localisedUrls: LocalisedUrlsMap) => void;
3
+ export declare const applyLocalisedUrlsToRoutes: (routes: LocalisedRoute[], languages: string[], localisedUrls: LocalisedUrlsMap, projection?: 'physical' | 'canonical') => LocalisedRoute[];
@@ -0,0 +1,14 @@
1
+ export type LocalisedRoute = {
2
+ type: 'nested' | 'page';
3
+ path?: string;
4
+ id?: string;
5
+ children?: LocalisedRoute[];
6
+ [key: string]: any;
7
+ };
8
+ export type LocalisedUrlPathMap = Record<string, string>;
9
+ export type LocalisedUrlsMap = Record<string, LocalisedUrlPathMap>;
10
+ export type LocalisedUrlsOption = boolean | LocalisedUrlsMap;
11
+ export interface ResolvedLocalisedUrlsConfig {
12
+ enabled: boolean;
13
+ map: LocalisedUrlsMap;
14
+ }
@@ -1,11 +1,13 @@
1
1
  declare const MARKER_ATTRIBUTE = "data-modern-helmet";
2
2
  export type RendererHeadMarkerProps = Record<typeof MARKER_ATTRIBUTE, string>;
3
3
  export * from './helmetCompat.js';
4
- export declare const beginHeadRender: (context: object) => void;
5
- export declare const collectHeadRecord: <RecordType>(context: object, createRecord: () => RecordType, publish: (records: RecordType[]) => void) => RendererHeadMarkerProps | null | undefined;
4
+ export type ReportLateHead = (message: string) => void;
5
+ export declare const LATE_HEAD_MESSAGE = "<Helmet> rendered in a boundary that completed after the shell; head tags dropped. Move it above the Suspense boundary or set server.ssrByEntries[entry].mode to 'string'.";
6
+ export declare const beginHeadRender: (context: object, reportLateHead: ReportLateHead) => void;
7
+ export declare const collectHeadRecord: <RecordType>(context: object, createRecord: () => RecordType, publish: (records: RecordType[]) => void) => RendererHeadMarkerProps | undefined;
6
8
  export declare const collectHeadState: <RecordType, StateType>(context: object, createRecord: () => RecordType, derive: (records: RecordType[]) => StateType, target: {
7
9
  helmet?: StateType;
8
- }) => RendererHeadMarkerProps | null | undefined;
10
+ }) => RendererHeadMarkerProps | undefined;
9
11
  export declare const publishHeadRender: (context: object) => void;
10
12
  export declare const finishHeadRender: (context: object) => void;
11
13
  export declare const completeHeadRender: (context: object, html: string) => string;
@@ -106,7 +106,7 @@ export type OperationContractOptions = {
106
106
  requireOperationVersion?: boolean;
107
107
  onViolation?: (violation: OperationContractViolation) => void;
108
108
  };
109
- export type OperationContextSource = 'client' | 'server' | 'generated-client' | 'effect-adapter' | 'data-platform' | 'unknown';
109
+ export type OperationContextSource = 'client' | 'server' | 'generated-client' | 'effect-server' | 'data-platform' | 'unknown';
110
110
  export type OperationContext = {
111
111
  requestId?: string;
112
112
  operationId?: string;
@@ -8,7 +8,7 @@ export type ScriptChunkLike = {
8
8
  filename?: string;
9
9
  url?: string;
10
10
  };
11
- export declare function getMatchedRouteChunks<T>(routeManifest: RouteManifestLike | undefined, matchedRouteIds: string[], routeAssetToChunk: (asset: string) => T): T[];
11
+ export declare function getMatchedRouteChunks<T>(routeManifest: RouteManifestLike | undefined, matchedRouteIds: readonly string[], routeAssetToChunk: (asset: string) => T): T[];
12
12
  export declare const orderHydrationScriptChunks: <T extends ScriptChunkLike>({ asyncEntryChunks, collectedChunks, matchedRouteChunks, entryName, }: {
13
13
  asyncEntryChunks: T[];
14
14
  collectedChunks: T[];
@@ -17,7 +17,7 @@ export declare const orderHydrationScriptChunks: <T extends ScriptChunkLike>({ a
17
17
  }) => T[];
18
18
  export declare function injectBeforeHydrationEntryScript(template: string, scripts: string, entryName?: string): string;
19
19
  export declare function replaceChunkJsPlaceholder(template: string, scripts: string, entryName: string | undefined, placeholder: string): string;
20
- export declare function createRouteHydrationScriptTags(routeManifest: RouteManifestLike | undefined, matchedRouteIds: string[], entryName: string, options?: {
20
+ export declare function createRouteHydrationScriptTags(routeManifest: RouteManifestLike | undefined, matchedRouteIds: readonly string[], entryName: string, options?: {
21
21
  nonce?: string;
22
22
  template?: string;
23
23
  }): string;
@@ -47,9 +47,7 @@ export declare function createRouterPlugin<Hooks extends Record<string, unknown>
47
47
  name: string;
48
48
  registryHooks: Hooks;
49
49
  setup(api: {
50
- getRuntimeConfig: () => {
51
- router?: Record<string, unknown>;
52
- };
50
+ getRuntimeConfig: () => Record<string, unknown>;
53
51
  [key: string]: any;
54
52
  }): void;
55
53
  };
@@ -1,12 +1,17 @@
1
1
  import type { InternalRouterRuntimeState, InternalRouterServerSnapshot, RouterServerPrepareResult } from './routerStateTypes.js';
2
- export type { BuiltInRouterFramework, InternalRouterRuntimeState, InternalRouterServerSnapshot, RouterFramework, RouterLifecyclePhase, RouterRouteMatchSnapshot, RouterServerPrepareResult, } from './routerStateTypes.js';
2
+ export type { BuiltInRouterFramework, InternalRouterRuntimeState, InternalRouterServerSnapshot, RouterFramework, RouterLifecyclePhase, RouterLinkTarget, RouterNavigationCapability, RouterNavigationSnapshot, RouterRouteMatchSnapshot, RouterServerPrepareResult, } from './routerStateTypes.js';
3
3
  export declare function getRouterRuntimeState(runtimeContext: object): InternalRouterRuntimeState | undefined;
4
4
  export declare function getRouterServerSnapshot(runtimeContext: object): InternalRouterServerSnapshot | undefined;
5
- type RouterSnapshotLike = Partial<InternalRouterServerSnapshot>;
6
- export declare function createRouterServerSnapshot(state: RouterSnapshotLike): InternalRouterServerSnapshot;
7
- export declare function createRouterRuntimeState(state: InternalRouterRuntimeState): InternalRouterRuntimeState;
5
+ /** Capture prepared metadata once; live router updates cannot rewrite it. */
6
+ export declare function createRouterServerSnapshot(snapshot: InternalRouterServerSnapshot): InternalRouterServerSnapshot;
7
+ /**
8
+ * Observe the router runtime-state slot for the given runtime context.
9
+ * Returns an unsubscribe function; safe to call for a context that never
10
+ * receives a router.
11
+ */
12
+ export declare function subscribeRouterRuntimeState(runtimeContext: object, listener: () => void): () => void;
8
13
  export declare function applyRouterRuntimeState<Context extends object>(runtimeContext: Context, state: InternalRouterRuntimeState): Context;
9
14
  export declare function applyRouterServerPrepareResult<Context extends object>(runtimeContext: Context, result: RouterServerPrepareResult): Context;
10
- export declare function getRouterHydrationScripts(runtimeContext: object): string[];
11
- export declare function getRouterMatchedRouteIds(runtimeContext: object): string[] | undefined;
15
+ export declare function getRouterHydrationScripts(runtimeContext: object): readonly string[];
16
+ export declare function getRouterMatchedRouteIds(runtimeContext: object): readonly string[] | undefined;
12
17
  export declare function cleanupRouterRuntimeState(runtimeContext: object): Promise<void>;
@@ -5,6 +5,14 @@ type RouterLifecycleEvent = {
5
5
  basename?: string;
6
6
  router?: unknown;
7
7
  };
8
+ type PreparedRequest<RuntimeContext extends object = object> = {
9
+ runtimeContext: RuntimeContext;
10
+ routerResult?: {
11
+ statusCode?: number;
12
+ errors?: Record<string, unknown> | null;
13
+ };
14
+ };
15
+ export declare const ROUTER_CLEANUP_ERROR = "An error occurs during router runtime cleanup";
8
16
  /** Install fork policy through lifecycle hooks supplied by the native owner. */
9
17
  export declare function createRouterStatePlugin<Hooks extends Record<string, unknown>>({ registryHooks, }: {
10
18
  registryHooks: Hooks;
@@ -13,6 +21,10 @@ export declare function createRouterStatePlugin<Hooks extends Record<string, unk
13
21
  registryHooks: Hooks;
14
22
  setup(api: {
15
23
  onBeforeRender: (callback: (context: object) => void) => unknown;
24
+ onRenderPrepared: (callback: <RuntimeContext extends object>(info: PreparedRequest<RuntimeContext>) => PreparedRequest<RuntimeContext>) => unknown;
25
+ onRequestEnd: (callback: (info: {
26
+ runtimeContext: object;
27
+ }) => Promise<void>) => unknown;
16
28
  onAfterCreateRouter: (callback: (event: RouterLifecycleEvent) => void) => unknown;
17
29
  }): void;
18
30
  };
@@ -1,3 +1,4 @@
1
+ import type React from 'react';
1
2
  export type BuiltInRouterFramework = 'react-router' | 'tanstack';
2
3
  export type RouterFramework = BuiltInRouterFramework | (string & {});
3
4
  export interface RouterRouteMatchSnapshot {
@@ -7,34 +8,63 @@ export interface RouterRouteMatchSnapshot {
7
8
  params?: Record<string, string>;
8
9
  }
9
10
  export interface InternalRouterServerSnapshot {
10
- framework?: RouterFramework;
11
- basename?: string;
12
- statusCode?: number;
13
- errors?: Record<string, unknown>;
14
- routerData?: {
15
- loaderData?: Record<string, unknown>;
16
- errors?: Record<string, unknown>;
11
+ readonly framework?: RouterFramework;
12
+ readonly basename?: string;
13
+ readonly statusCode?: number;
14
+ readonly errors?: Record<string, unknown>;
15
+ readonly routerData?: {
16
+ readonly loaderData?: Record<string, unknown>;
17
+ readonly errors?: Record<string, unknown>;
17
18
  };
18
- hydrationScript?: string;
19
- hydrationScripts?: string[];
20
- matchedRouteIds?: string[];
21
- matches?: RouterRouteMatchSnapshot[];
19
+ readonly hydrationScripts?: readonly string[];
20
+ readonly matchedRouteIds?: readonly string[];
21
+ readonly matches?: readonly RouterRouteMatchSnapshot[];
22
22
  }
23
23
  export interface InternalRouterRuntimeState {
24
24
  framework: RouterFramework;
25
25
  basename?: string;
26
26
  instance?: unknown;
27
- hydrationScript?: string;
28
- hydrationScripts?: string[];
29
- matchedRouteIds?: string[];
30
- matches?: RouterRouteMatchSnapshot[];
31
- serverSnapshot?: InternalRouterServerSnapshot;
27
+ navigation?: RouterNavigationCapability;
32
28
  cleanup?: () => void | Promise<void>;
33
29
  }
34
30
  export interface RouterServerPrepareResult {
35
31
  state: InternalRouterRuntimeState;
36
32
  snapshot?: InternalRouterServerSnapshot;
37
- redirect?: Response;
38
33
  cleanup?: () => void | Promise<void>;
39
34
  }
40
35
  export type RouterLifecyclePhase = 'ssr-prepare' | 'client-create' | 'hydrate';
36
+ /** Provider-owned browser navigation. Consumers never inspect router internals. */
37
+ export interface RouterNavigationSnapshot {
38
+ location: {
39
+ pathname: string;
40
+ search: string;
41
+ hash: string;
42
+ };
43
+ params: Record<string, string>;
44
+ }
45
+ export interface RouterLinkTarget {
46
+ pathname: string;
47
+ href: string;
48
+ search?: Record<string, unknown>;
49
+ hash?: string;
50
+ hashScrollIntoView?: boolean | ScrollIntoViewOptions;
51
+ prefetch?: 'intent' | 'render' | 'viewport' | 'none';
52
+ preload?: unknown;
53
+ }
54
+ export interface RouterNavigationCapability {
55
+ getSnapshot: () => RouterNavigationSnapshot;
56
+ subscribe: (listener: () => void) => () => void;
57
+ navigate: (href: string, options?: {
58
+ replace?: boolean;
59
+ state?: unknown;
60
+ }) => void | Promise<void>;
61
+ Link: React.ComponentType<{
62
+ to: string;
63
+ children?: React.ReactNode;
64
+ [key: string]: unknown;
65
+ }>;
66
+ createLinkProps?: (target: RouterLinkTarget) => {
67
+ to: string;
68
+ [key: string]: unknown;
69
+ };
70
+ }