@bleedingdev/modern-js-renderer-solid 3.9.3-ultramodern.8 → 3.9.3-ultramodern.9

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 (89) hide show
  1. package/dist/esm/actions.js +2 -2
  2. package/dist/esm/application-router.js +7 -2
  3. package/dist/esm/entry-client.js +1 -0
  4. package/dist/esm/federation-ssr.js +17 -9
  5. package/dist/esm/federation.js +8 -4
  6. package/dist/esm/i18n/LocalizedLink.js +28 -50
  7. package/dist/esm/i18n/index.js +1 -1
  8. package/dist/esm/index.js +1 -1
  9. package/dist/esm/route-completion.js +2 -1
  10. package/dist/esm/router-binding/Asset.js +3 -2
  11. package/dist/esm/router-binding/Matches.js +19 -12
  12. package/dist/esm/router-binding/PROVENANCE.json +1 -1
  13. package/dist/esm/router-binding/RouterProvider.js +2 -1
  14. package/dist/esm/router-binding/Transitioner.js +10 -4
  15. package/dist/esm/router-binding/fileRoute.js +1 -1
  16. package/dist/esm/router-binding/headContentUtils.js +9 -3
  17. package/dist/esm/router-binding/index.js +1 -1
  18. package/dist/esm/router-binding/link.js +2 -2
  19. package/dist/esm/router-binding/registryTransfer.js +16 -3
  20. package/dist/esm/router-binding/router.js +2 -1
  21. package/dist/esm/router-binding/routerOptions.js +0 -0
  22. package/dist/esm/router-binding/useLoaderData.js +1 -2
  23. package/dist/esm/router-binding/useNavigate.js +8 -2
  24. package/dist/esm/router-binding/useParams.js +1 -3
  25. package/dist/esm/router-binding/useSearch.js +1 -3
  26. package/dist/esm-node/actions.js +2 -2
  27. package/dist/esm-node/application-router.js +7 -2
  28. package/dist/esm-node/entry-client.js +1 -0
  29. package/dist/esm-node/federation-ssr.js +17 -9
  30. package/dist/esm-node/federation.js +8 -4
  31. package/dist/esm-node/i18n/LocalizedLink.js +43 -97
  32. package/dist/esm-node/i18n/index.js +1 -1
  33. package/dist/esm-node/index.js +1 -1
  34. package/dist/esm-node/plugin/profile.js +2 -1
  35. package/dist/esm-node/route-completion.js +2 -1
  36. package/dist/esm-node/router-binding/Asset.js +3 -2
  37. package/dist/esm-node/router-binding/Matches.js +30 -30
  38. package/dist/esm-node/router-binding/PROVENANCE.json +1 -1
  39. package/dist/esm-node/router-binding/RouterProvider.js +2 -1
  40. package/dist/esm-node/router-binding/Transitioner.js +10 -4
  41. package/dist/esm-node/router-binding/fileRoute.js +1 -1
  42. package/dist/esm-node/router-binding/headContentUtils.js +9 -3
  43. package/dist/esm-node/router-binding/index.js +1 -1
  44. package/dist/esm-node/router-binding/link.js +2 -2
  45. package/dist/esm-node/router-binding/registryTransfer.js +16 -3
  46. package/dist/esm-node/router-binding/router.js +2 -1
  47. package/dist/esm-node/router-binding/routerOptions.js +1 -0
  48. package/dist/esm-node/router-binding/useLoaderData.js +1 -2
  49. package/dist/esm-node/router-binding/useNavigate.js +8 -2
  50. package/dist/esm-node/router-binding/useParams.js +1 -3
  51. package/dist/esm-node/router-binding/useSearch.js +1 -3
  52. package/dist/types/federation-ssr.d.ts +3 -5
  53. package/dist/types/i18n/LocalizedLink.d.ts +5 -33
  54. package/dist/types/i18n/index.d.ts +1 -1
  55. package/dist/types/index.d.ts +1 -1
  56. package/dist/types/router-binding/RouterProvider.d.ts +2 -1
  57. package/dist/types/router-binding/index.d.ts +3 -2
  58. package/dist/types/router-binding/registryTransfer.d.ts +2 -0
  59. package/dist/types/router-binding/routerOptions.d.ts +6 -0
  60. package/dist/types/router.d.ts +3 -3
  61. package/package.json +19 -10
  62. package/src/actions.tsx +5 -1
  63. package/src/application-router.tsx +9 -3
  64. package/src/entry-client.tsx +1 -0
  65. package/src/federation-ssr.ts +32 -13
  66. package/src/federation.tsx +9 -5
  67. package/src/i18n/LocalizedLink.tsx +44 -83
  68. package/src/i18n/index.ts +1 -5
  69. package/src/index.ts +1 -1
  70. package/src/plugin/profile.ts +3 -1
  71. package/src/route-completion.tsx +5 -1
  72. package/src/router-binding/Asset.tsx +10 -9
  73. package/src/router-binding/Matches.tsx +10 -10
  74. package/src/router-binding/PROVENANCE.json +1 -1
  75. package/src/router-binding/RouterProvider.tsx +4 -5
  76. package/src/router-binding/Transitioner.tsx +6 -1
  77. package/src/router-binding/fileRoute.ts +1 -1
  78. package/src/router-binding/headContentUtils.tsx +5 -3
  79. package/src/router-binding/index.tsx +1 -4
  80. package/src/router-binding/link.tsx +2 -2
  81. package/src/router-binding/registryTransfer.ts +23 -6
  82. package/src/router-binding/route.tsx +1 -1
  83. package/src/router-binding/router.ts +2 -0
  84. package/src/router-binding/routerOptions.ts +26 -0
  85. package/src/router-binding/useLoaderData.tsx +4 -3
  86. package/src/router-binding/useNavigate.tsx +5 -3
  87. package/src/router-binding/useParams.tsx +1 -3
  88. package/src/router-binding/useSearch.tsx +1 -3
  89. package/src/router.ts +3 -3
@@ -62,7 +62,7 @@ function InlineCssStyle({
62
62
  attrs,
63
63
  children,
64
64
  }: {
65
- attrs?: Record<string, any>;
65
+ attrs: Record<string, any> | undefined;
66
66
  children?: RouterManagedTag['children'];
67
67
  }) {
68
68
  const isInlineCssPlaceholder = children === undefined;
@@ -85,7 +85,7 @@ function InlineCssStyle({
85
85
 
86
86
  function HeadElement(props: {
87
87
  tag: 'meta' | 'link' | 'style';
88
- attrs?: Record<string, any>;
88
+ attrs: Record<string, any> | undefined;
89
89
  children?: unknown;
90
90
  }): JSX.Element | null {
91
91
  // Each branch must live in its own `case` block so the Solid compiler emits
@@ -101,11 +101,12 @@ function HeadElement(props: {
101
101
  let element: Element;
102
102
  switch (props.tag) {
103
103
  case 'style': {
104
- const attrs = {
105
- ...props.attrs,
106
- innerHTML:
107
- typeof props.children === 'string' ? props.children : undefined,
108
- };
104
+ const attrs = { ...props.attrs };
105
+ // The managed child text owns this field, including when it is absent.
106
+ delete attrs.innerHTML;
107
+ if (typeof props.children === 'string') {
108
+ attrs.innerHTML = props.children;
109
+ }
109
110
  element = (<style {...attrs} />) as unknown as Element;
110
111
  break;
111
112
  }
@@ -125,7 +126,7 @@ function HeadElement(props: {
125
126
  }
126
127
 
127
128
  function Title(props: {
128
- attrs?: Record<string, any>;
129
+ attrs: Record<string, any> | undefined;
129
130
  children?: unknown;
130
131
  }): JSX.Element | null {
131
132
  let el: HTMLTitleElement | undefined;
@@ -158,7 +159,7 @@ function Title(props: {
158
159
  }
159
160
 
160
161
  function Script(props: {
161
- attrs?: Record<string, any>;
162
+ attrs: Record<string, any> | undefined;
162
163
  children?: unknown;
163
164
  }): JSX.Element | null {
164
165
  const router = useRouter();
@@ -79,16 +79,16 @@ function MatchesInner() {
79
79
  getResetKey={() => router.stores.matches.get()}
80
80
  render={matchContent}
81
81
  errorComponent={ErrorComponent}
82
- onCatch={
83
- process.env.NODE_ENV !== 'production'
84
- ? error => {
82
+ {...(process.env.NODE_ENV !== 'production'
83
+ ? {
84
+ onCatch: (error: Error) => {
85
85
  console.warn(
86
86
  `Warning: The following error wasn't caught by any route! At the very least, consider setting an 'errorComponent' in your RootRoute!`,
87
87
  );
88
88
  console.warn(`Warning: ${error.message || error.toString()}`);
89
- }
90
- : undefined
91
- }
89
+ },
90
+ }
91
+ : {})}
92
92
  />
93
93
  </NearestMatchContext>
94
94
  );
@@ -126,10 +126,10 @@ export function useMatchRoute<TRouter extends AnyRouter = RegisteredRouter>() {
126
126
  router.stores.resolvedLocation.get();
127
127
  router.stores.status.get();
128
128
  return router.matchRoute(rest as any, {
129
- pending,
130
- caseSensitive,
131
- fuzzy,
132
- includeSearch,
129
+ ...(pending !== undefined ? { pending } : {}),
130
+ ...(caseSensitive !== undefined ? { caseSensitive } : {}),
131
+ ...(fuzzy !== undefined ? { fuzzy } : {}),
132
+ ...(includeSearch !== undefined ? { includeSearch } : {}),
133
133
  });
134
134
  });
135
135
  };
@@ -67,7 +67,7 @@
67
67
  "utils.ts": "a398c76ac378efe6f8b9aa859a5335d4f07cc692a31754fa93c8bf3e5df0b6ff"
68
68
  },
69
69
  "localChanges": [
70
- "The public router surface omits InjectedHtmlEntry, isPlainArray and isPlainObject, which router-core 1.171.37 no longer exports. SSR options use the current top-level serializationAdapters contract and the public Solid 2 option types without legacy fallbacks or type assertions.",
70
+ "The public router surface omits InjectedHtmlEntry, isPlainArray and isPlainObject, which router-core 1.171.37 no longer exports. Managed SSR uses immutable public route data and native Solid 2 promise serialization. Custom serializationAdapters are unsupported and rejected before router construction or document commitment; the local public option types do not admit them.",
71
71
  "Authored JSX types import @solidjs/web and have explicit return annotations for Solid 2 declaration generation.",
72
72
  "Hydration state and keyed match transfer use Solid 2 RC.13 public isHydrating, isHydratable, getHydrationWriter and takeHydrationValue APIs.",
73
73
  "Explicit public beforeLoad results transfer after canonical validation; private request context stays in owner closures. Initial hydration inherits checked server beforeLoad contributions, invokes the native client route-context callback and preserves client options context without rerunning initial beforeLoad callbacks or loaders.",
@@ -1,18 +1,16 @@
1
1
  import type { JSX } from '@solidjs/web';
2
- import type {
3
- AnyRouter,
4
- RegisteredRouter,
5
- RouterOptions,
6
- } from '@tanstack/router-core';
2
+ import type { AnyRouter, RegisteredRouter } from '@tanstack/router-core';
7
3
  import { isServer } from '@tanstack/router-core/isServer';
8
4
  import * as Solid from 'solid-js';
9
5
  import { setupFlightDataConsumer } from './flightData';
10
6
  import { Matches } from './Matches';
11
7
  import {
8
+ assertNativeSerializationOptions,
12
9
  requireManagedPublicRouter,
13
10
  serializeMatchTransfer,
14
11
  } from './registryTransfer';
15
12
  import { routerContext } from './routerContext';
13
+ import type { RouterOptions } from './routerOptions';
16
14
  import { SafeFragment } from './SafeFragment';
17
15
 
18
16
  const RouterContext = routerContext as unknown as Solid.Component<{
@@ -30,6 +28,7 @@ export function RouterContextProvider<
30
28
  }: RouterProps<TRouter, TDehydrated> & {
31
29
  children: () => JSX.Element;
32
30
  }) {
31
+ assertNativeSerializationOptions(rest);
33
32
  requireManagedPublicRouter(router);
34
33
  if (Object.keys(rest).length > 0) {
35
34
  Solid.runWithOwner(null, () => {
@@ -162,9 +162,14 @@ export function Rendered() {
162
162
  Solid.onSettled(() => {
163
163
  const resolvedLocation = getResolvedLocation(router);
164
164
  if (resolvedLocation) {
165
+ const { fromLocation, ...changeInfo } = getLocationChangeInfo(
166
+ resolvedLocation,
167
+ resolvedLocation,
168
+ );
165
169
  router.emit({
166
170
  type: 'onRendered',
167
- ...getLocationChangeInfo(resolvedLocation, resolvedLocation),
171
+ ...changeInfo,
172
+ ...(fromLocation !== undefined ? { fromLocation } : {}),
168
173
  });
169
174
  }
170
175
  });
@@ -69,7 +69,7 @@ export class FileRoute<
69
69
  public path?: TFilePath,
70
70
  _opts?: { silent: boolean },
71
71
  ) {
72
- this.silent = _opts?.silent;
72
+ if (_opts) this.silent = _opts.silent;
73
73
  }
74
74
 
75
75
  createRoute = <
@@ -154,7 +154,9 @@ export const useTags = (assetCrossOrigin?: AssetCrossOriginConfig) => {
154
154
  ...manifest.inlineStyle.attrs,
155
155
  nonce,
156
156
  },
157
- children: manifest.inlineStyle.children,
157
+ ...(manifest.inlineStyle.children !== undefined
158
+ ? { children: manifest.inlineStyle.children }
159
+ : {}),
158
160
  inlineCss: true,
159
161
  });
160
162
  }
@@ -192,7 +194,7 @@ export const useTags = (assetCrossOrigin?: AssetCrossOriginConfig) => {
192
194
  ...attrs,
193
195
  nonce,
194
196
  },
195
- children: children as string | undefined,
197
+ ...(children != null ? { children: String(children) } : {}),
196
198
  })) satisfies Array<RouterManagedTag>;
197
199
  });
198
200
 
@@ -206,7 +208,7 @@ export const useTags = (assetCrossOrigin?: AssetCrossOriginConfig) => {
206
208
  ...attrs,
207
209
  nonce,
208
210
  },
209
- children: children as string | undefined,
211
+ ...(children != null ? { children: String(children) } : {}),
210
212
  })) satisfies Array<RouterManagedTag>;
211
213
  });
212
214
 
@@ -26,7 +26,6 @@ export type {
26
26
  AnyRouterWithContext,
27
27
  AnyRouteWithContext,
28
28
  AnySchema,
29
- AnySerializationAdapter,
30
29
  AnyValidator,
31
30
  AnyValidatorAdapter,
32
31
  AnyValidatorFn,
@@ -153,7 +152,6 @@ export type {
153
152
  RouterEvents,
154
153
  RouterListener,
155
154
  RouterManagedTag,
156
- RouterOptions,
157
155
  RouterState,
158
156
  RoutesById,
159
157
  RoutesByPath,
@@ -163,7 +161,6 @@ export type {
163
161
  SearchSchemaInput,
164
162
  SearchSerializer,
165
163
  SerializableExtensions,
166
- SerializationAdapter,
167
164
  StaticDataRouteOption,
168
165
  StringifyParamsFn,
169
166
  ToMaskOptions,
@@ -197,7 +194,6 @@ export {
197
194
  cleanPath,
198
195
  composeRewrites,
199
196
  createControlledPromise,
200
- createSerializationAdapter,
201
197
  DEFAULT_PROTOCOL_ALLOWLIST,
202
198
  deepEqual,
203
199
  defaultParseSearch,
@@ -291,6 +287,7 @@ export {
291
287
  rootRouteWithContext,
292
288
  } from './route';
293
289
  export { createRouter, Router } from './router';
290
+ export type { RouterOptions } from './routerOptions';
294
291
  export { ScriptOnce } from './ScriptOnce';
295
292
  export { Scripts } from './Scripts';
296
293
  export {
@@ -505,9 +505,9 @@ export function useLinkProps<
505
505
  }
506
506
 
507
507
  const defineGetters = (getters: Record<string, () => any>) => {
508
- for (const key of Object.keys(getters)) {
508
+ for (const [key, get] of Object.entries(getters)) {
509
509
  Object.defineProperty(linkProps, key, {
510
- get: getters[key],
510
+ get,
511
511
  enumerable: true,
512
512
  configurable: true,
513
513
  });
@@ -78,8 +78,21 @@ export function registerPublicRouteDataOwner(
78
78
  });
79
79
  }
80
80
 
81
+ /** Managed match transfer uses the immutable public-data contract, not custom adapters. */
82
+ export function assertNativeSerializationOptions(options: object): void {
83
+ if ('serializationAdapters' in options) {
84
+ const error = new DataProtocolError(
85
+ 'Solid serializationAdapters are unsupported by the immutable public-data profile; return public records, arrays and scalars instead',
86
+ );
87
+ const owner: RequestSession | undefined = getRequestEvent()?.locals.session;
88
+ owner?.fail(error);
89
+ throw error;
90
+ }
91
+ }
92
+
81
93
  /** Framework SSR accepts only routers that passed its public-data factory boundary. */
82
94
  export function requireManagedPublicRouter(router: AnyRouter): void {
95
+ assertNativeSerializationOptions(router.options);
83
96
  const owner: RequestSession | undefined = getRequestEvent()?.locals.session;
84
97
  if (
85
98
  !owner ||
@@ -268,6 +281,11 @@ export function primeRouterFromRegistry(router: AnyRouter): boolean {
268
281
  try {
269
282
  for (const [index, match] of matches.entries()) {
270
283
  const entry = entries[index]!;
284
+ const beforeLoadContext = preparePublicContextData(
285
+ entry.beforeLoadContext,
286
+ owner,
287
+ privateContexts,
288
+ ) as Record<string, unknown> | undefined;
271
289
  const checked: TransferredMatch = {
272
290
  ...entry,
273
291
  loaderData: prepareHydratedLoaderData(
@@ -275,11 +293,7 @@ export function primeRouterFromRegistry(router: AnyRouter): boolean {
275
293
  owner,
276
294
  privateContexts,
277
295
  ),
278
- beforeLoadContext: preparePublicContextData(
279
- entry.beforeLoadContext,
280
- owner,
281
- privateContexts,
282
- ) as Record<string, unknown> | undefined,
296
+ ...(beforeLoadContext !== undefined ? { beforeLoadContext } : {}),
283
297
  error: preparePublicContextData(entry.error, owner, privateContexts),
284
298
  };
285
299
  entries[index] = checked;
@@ -356,7 +370,10 @@ function applyTransferredMatch(
356
370
  __beforeLoadContext: entry.beforeLoadContext,
357
371
  } as AnyRouteMatch;
358
372
  if ('loaderData' in entry) next.loaderData = entry.loaderData;
359
- if ('ssr' in entry) next.ssr = entry.ssr;
373
+ if ('ssr' in entry) {
374
+ if (entry.ssr === undefined) delete next.ssr;
375
+ else next.ssr = entry.ssr;
376
+ }
360
377
  return next;
361
378
  }
362
379
 
@@ -133,7 +133,7 @@ export class RouteApi<
133
133
  return useNavigate({ from: router.routesById[this.id as string].fullPath });
134
134
  };
135
135
 
136
- notFound = (opts?: NotFoundError) => {
136
+ override notFound = (opts?: NotFoundError) => {
137
137
  return notFound({ routeId: this.id as string, ...opts });
138
138
  };
139
139
 
@@ -9,6 +9,7 @@ import type {
9
9
  import { RouterCore } from '@tanstack/router-core';
10
10
  import { isServer } from '@tanstack/router-core/isServer';
11
11
  import {
12
+ assertNativeSerializationOptions,
12
13
  installPublicContextPublication,
13
14
  primeRouterFromRegistry,
14
15
  } from './registryTransfer';
@@ -104,6 +105,7 @@ export class Router<
104
105
  TDehydrated
105
106
  >,
106
107
  ) {
108
+ assertNativeSerializationOptions(options);
107
109
  super(options, getStoreFactory);
108
110
  installPublicContextPublication(this);
109
111
  // The hydration-claiming boot (see registryTransfer). Router creation is
@@ -0,0 +1,26 @@
1
+ import type { RouterHistory } from '@tanstack/history';
2
+ import type {
3
+ AnyRoute,
4
+ RouterOptions as CoreRouterOptions,
5
+ TrailingSlashOption,
6
+ } from '@tanstack/router-core';
7
+
8
+ /** Native managed hydration transfers immutable public data only. */
9
+ export type RouterOptions<
10
+ TRouteTree extends AnyRoute,
11
+ TTrailingSlashOption extends TrailingSlashOption,
12
+ TDefaultStructuralSharingOption extends boolean = false,
13
+ TRouterHistory extends RouterHistory = RouterHistory,
14
+ TDehydrated = undefined,
15
+ > = Omit<
16
+ CoreRouterOptions<
17
+ TRouteTree,
18
+ TTrailingSlashOption,
19
+ TDefaultStructuralSharingOption,
20
+ TRouterHistory,
21
+ TDehydrated
22
+ >,
23
+ 'serializationAdapters'
24
+ > & {
25
+ serializationAdapters?: never;
26
+ };
@@ -41,10 +41,11 @@ export function useLoaderData<
41
41
  opts: UseLoaderDataOptions<TRouter, TFrom, TStrict, TSelected>,
42
42
  ): Accessor<UseLoaderDataResult<TRouter, TFrom, TStrict, TSelected>> {
43
43
  return useMatch({
44
- from: opts.from!,
45
- strict: opts.strict,
44
+ ...opts,
46
45
  select: match => {
47
- return opts.select ? opts.select(match.loaderData) : match.loaderData;
46
+ // The hook reads the rendered match; router-core also types pending
47
+ // matches, where loaderData is optional, in this shared match shape.
48
+ return opts.select ? opts.select(match.loaderData!) : match.loaderData;
48
49
  },
49
50
  }) as any;
50
51
  }
@@ -16,12 +16,14 @@ export function useNavigate<
16
16
  }): UseNavigateResult<TDefaultFrom> {
17
17
  const router = useRouter();
18
18
 
19
- return ((options: NavigateOptions) => {
19
+ const navigate: UseNavigateResult<TDefaultFrom> = options => {
20
+ const from = options.from ?? _defaultOpts?.from;
20
21
  return router.navigate({
21
22
  ...options,
22
- from: options.from ?? _defaultOpts?.from,
23
+ ...(from === undefined ? {} : { from }),
23
24
  });
24
- }) as UseNavigateResult<TDefaultFrom>;
25
+ };
26
+ return navigate;
25
27
  }
26
28
 
27
29
  export function Navigate<
@@ -61,9 +61,7 @@ export function useParams<
61
61
  ThrowOrOptional<UseParamsResult<TRouter, TFrom, TStrict, TSelected>, TThrow>
62
62
  > {
63
63
  return useMatch({
64
- from: opts.from!,
65
- strict: opts.strict,
66
- shouldThrow: opts.shouldThrow,
64
+ ...opts,
67
65
  select: match => {
68
66
  const params = opts.strict === false ? match.params : match._strictParams;
69
67
  return opts.select ? opts.select(params) : params;
@@ -61,9 +61,7 @@ export function useSearch<
61
61
  ThrowOrOptional<UseSearchResult<TRouter, TFrom, TStrict, TSelected>, TThrow>
62
62
  > {
63
63
  return useMatch({
64
- from: opts.from!,
65
- strict: opts.strict,
66
- shouldThrow: opts.shouldThrow,
64
+ ...opts,
67
65
  select: match => {
68
66
  const search = match.search;
69
67
  return opts.select ? opts.select(search) : search;
package/src/router.ts CHANGED
@@ -77,11 +77,11 @@ export interface FileSystemRouteModule {
77
77
  pendingComponent?: RouteComponent;
78
78
  errorComponent?: ErrorRouteComponent;
79
79
  notFoundComponent?: NotFoundRouteComponent;
80
- head?: AnyRoute['options']['head'];
80
+ head?: NonNullable<AnyRoute['options']['head']>;
81
81
  /** Explicit returned context becomes immutable public native route context. */
82
- beforeLoad?: AnyRoute['options']['beforeLoad'];
82
+ beforeLoad?: NonNullable<AnyRoute['options']['beforeLoad']>;
83
83
  /** Native synchronous context contributions are checked before composition. */
84
- context?: AnyRoute['options']['context'];
84
+ context?: NonNullable<AnyRoute['options']['context']>;
85
85
  validateSearch?: (search: Record<string, unknown>) => Record<string, unknown>;
86
86
  }
87
87