@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.
- package/dist/esm/actions.js +2 -2
- package/dist/esm/application-router.js +7 -2
- package/dist/esm/entry-client.js +1 -0
- package/dist/esm/federation-ssr.js +17 -9
- package/dist/esm/federation.js +8 -4
- package/dist/esm/i18n/LocalizedLink.js +28 -50
- package/dist/esm/i18n/index.js +1 -1
- package/dist/esm/index.js +1 -1
- package/dist/esm/route-completion.js +2 -1
- package/dist/esm/router-binding/Asset.js +3 -2
- package/dist/esm/router-binding/Matches.js +19 -12
- package/dist/esm/router-binding/PROVENANCE.json +1 -1
- package/dist/esm/router-binding/RouterProvider.js +2 -1
- package/dist/esm/router-binding/Transitioner.js +10 -4
- package/dist/esm/router-binding/fileRoute.js +1 -1
- package/dist/esm/router-binding/headContentUtils.js +9 -3
- package/dist/esm/router-binding/index.js +1 -1
- package/dist/esm/router-binding/link.js +2 -2
- package/dist/esm/router-binding/registryTransfer.js +16 -3
- package/dist/esm/router-binding/router.js +2 -1
- package/dist/esm/router-binding/routerOptions.js +0 -0
- package/dist/esm/router-binding/useLoaderData.js +1 -2
- package/dist/esm/router-binding/useNavigate.js +8 -2
- package/dist/esm/router-binding/useParams.js +1 -3
- package/dist/esm/router-binding/useSearch.js +1 -3
- package/dist/esm-node/actions.js +2 -2
- package/dist/esm-node/application-router.js +7 -2
- package/dist/esm-node/entry-client.js +1 -0
- package/dist/esm-node/federation-ssr.js +17 -9
- package/dist/esm-node/federation.js +8 -4
- package/dist/esm-node/i18n/LocalizedLink.js +43 -97
- package/dist/esm-node/i18n/index.js +1 -1
- package/dist/esm-node/index.js +1 -1
- package/dist/esm-node/plugin/profile.js +2 -1
- package/dist/esm-node/route-completion.js +2 -1
- package/dist/esm-node/router-binding/Asset.js +3 -2
- package/dist/esm-node/router-binding/Matches.js +30 -30
- package/dist/esm-node/router-binding/PROVENANCE.json +1 -1
- package/dist/esm-node/router-binding/RouterProvider.js +2 -1
- package/dist/esm-node/router-binding/Transitioner.js +10 -4
- package/dist/esm-node/router-binding/fileRoute.js +1 -1
- package/dist/esm-node/router-binding/headContentUtils.js +9 -3
- package/dist/esm-node/router-binding/index.js +1 -1
- package/dist/esm-node/router-binding/link.js +2 -2
- package/dist/esm-node/router-binding/registryTransfer.js +16 -3
- package/dist/esm-node/router-binding/router.js +2 -1
- package/dist/esm-node/router-binding/routerOptions.js +1 -0
- package/dist/esm-node/router-binding/useLoaderData.js +1 -2
- package/dist/esm-node/router-binding/useNavigate.js +8 -2
- package/dist/esm-node/router-binding/useParams.js +1 -3
- package/dist/esm-node/router-binding/useSearch.js +1 -3
- package/dist/types/federation-ssr.d.ts +3 -5
- package/dist/types/i18n/LocalizedLink.d.ts +5 -33
- package/dist/types/i18n/index.d.ts +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/router-binding/RouterProvider.d.ts +2 -1
- package/dist/types/router-binding/index.d.ts +3 -2
- package/dist/types/router-binding/registryTransfer.d.ts +2 -0
- package/dist/types/router-binding/routerOptions.d.ts +6 -0
- package/dist/types/router.d.ts +3 -3
- package/package.json +19 -10
- package/src/actions.tsx +5 -1
- package/src/application-router.tsx +9 -3
- package/src/entry-client.tsx +1 -0
- package/src/federation-ssr.ts +32 -13
- package/src/federation.tsx +9 -5
- package/src/i18n/LocalizedLink.tsx +44 -83
- package/src/i18n/index.ts +1 -5
- package/src/index.ts +1 -1
- package/src/plugin/profile.ts +3 -1
- package/src/route-completion.tsx +5 -1
- package/src/router-binding/Asset.tsx +10 -9
- package/src/router-binding/Matches.tsx +10 -10
- package/src/router-binding/PROVENANCE.json +1 -1
- package/src/router-binding/RouterProvider.tsx +4 -5
- package/src/router-binding/Transitioner.tsx +6 -1
- package/src/router-binding/fileRoute.ts +1 -1
- package/src/router-binding/headContentUtils.tsx +5 -3
- package/src/router-binding/index.tsx +1 -4
- package/src/router-binding/link.tsx +2 -2
- package/src/router-binding/registryTransfer.ts +23 -6
- package/src/router-binding/route.tsx +1 -1
- package/src/router-binding/router.ts +2 -0
- package/src/router-binding/routerOptions.ts +26 -0
- package/src/router-binding/useLoaderData.tsx +4 -3
- package/src/router-binding/useNavigate.tsx +5 -3
- package/src/router-binding/useParams.tsx +1 -3
- package/src/router-binding/useSearch.tsx +1 -3
- package/src/router.ts +3 -3
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import { useMatch } from "./useMatch.js";
|
|
2
2
|
function useSearch(opts) {
|
|
3
3
|
return useMatch({
|
|
4
|
-
|
|
5
|
-
strict: opts.strict,
|
|
6
|
-
shouldThrow: opts.shouldThrow,
|
|
4
|
+
...opts,
|
|
7
5
|
select: (match)=>{
|
|
8
6
|
const search = match.search;
|
|
9
7
|
return opts.select ? opts.select(search) : search;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { FederatedAssets } from '@modern-js/renderer-core/federation';
|
|
1
2
|
import type { SolidFederationScope } from './federation-context.js';
|
|
2
3
|
/**
|
|
3
4
|
* The server half of same-renderer Module Federation. federatedComponent()
|
|
@@ -6,11 +7,8 @@ import type { SolidFederationScope } from './federation-context.js';
|
|
|
6
7
|
*/
|
|
7
8
|
/** Asset keys for server-rendered remotes, e.g. `ultramodern-federation:remote/Widget`. */
|
|
8
9
|
export declare const FEDERATED_ASSET_PREFIX = "ultramodern-federation:";
|
|
9
|
-
/**
|
|
10
|
-
export declare function
|
|
11
|
-
hydration: string;
|
|
12
|
-
assets: import("@modern-js/renderer-core/federation").FederatedAssets;
|
|
13
|
-
} | undefined;
|
|
10
|
+
/** Retain the assets admitted with this module until its response resolves them. */
|
|
11
|
+
export declare function captureFederatedServerAssets(scope: SolidFederationScope, id: string, assets: FederatedAssets | undefined): string | undefined;
|
|
14
12
|
export declare function federatedAssetKey(id: string): string;
|
|
15
13
|
/**
|
|
16
14
|
* Extend a static Solid asset manifest with the remotes this response
|
|
@@ -1,43 +1,15 @@
|
|
|
1
1
|
import type { JSX } from '@solidjs/web';
|
|
2
|
-
import { type LinkComponentProps } from '../router.js';
|
|
3
|
-
export interface LocalizedLinkProps {
|
|
2
|
+
import { type AnyRouter, type LinkComponentProps } from '../router.js';
|
|
3
|
+
export interface LocalizedLinkProps extends Omit<LinkComponentProps<'a', AnyRouter, string, string>, 'to' | 'mask' | 'href' | 'publicHref' | '_fromLocation'> {
|
|
4
4
|
/** A canonical, language-agnostic target, e.g. `/products/red-shoe`. */
|
|
5
5
|
to: string;
|
|
6
6
|
/** Localize for a specific language instead of the current one. */
|
|
7
7
|
language?: string;
|
|
8
|
-
children?: LinkComponentProps<'a'>['children'];
|
|
9
|
-
class?: string;
|
|
10
|
-
activeProps?: LinkComponentProps<'a'>['activeProps'];
|
|
11
|
-
inactiveProps?: LinkComponentProps<'a'>['inactiveProps'];
|
|
12
|
-
activeOptions?: LinkComponentProps<'a'>['activeOptions'];
|
|
13
|
-
replace?: boolean;
|
|
14
|
-
preload?: LinkComponentProps<'a'>['preload'];
|
|
15
8
|
[key: `data-${string}`]: unknown;
|
|
16
9
|
}
|
|
17
10
|
/**
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* `Link`. It resolves the href itself via the shared pathname helpers, so it
|
|
22
|
-
* works with or without the router's i18n `rewrite`. Every link, including
|
|
23
|
-
* one to another language, is the native `Link`: navigation, preloading and
|
|
24
|
-
* active state stay with the router.
|
|
25
|
-
*
|
|
26
|
-
* Under the i18n `rewrite` the router matches canonical paths and localizes
|
|
27
|
-
* outgoing locations to the current language, so a link to another language
|
|
28
|
-
* routes to the canonical `to` and publishes the target language's URL as a
|
|
29
|
-
* route mask (the rewrite keeps a mask's explicit language). The entry's
|
|
30
|
-
* router language synchronization (`syncWithRouter`) switches the i18next
|
|
31
|
-
* instance from the navigated URL, as it does for history navigation.
|
|
32
|
-
*
|
|
33
|
-
* Props are forwarded explicitly (not via object-rest or a missing
|
|
34
|
-
* `splitProps`/`mergeProps` primitive — Solid 2 only exports `merge`) so
|
|
35
|
-
* each one is read directly from `props` in JSX position and keeps its
|
|
36
|
-
* per-key reactivity.
|
|
11
|
+
* The native router owns navigation, preloading and active state. A route mask
|
|
12
|
+
* publishes the selected language URL while keeping the canonical route;
|
|
13
|
+
* the entry's router subscription synchronizes the i18next instance.
|
|
37
14
|
*/
|
|
38
15
|
export declare function LocalizedLink(props: LocalizedLinkProps): JSX.Element;
|
|
39
|
-
/**
|
|
40
|
-
* Language-invariant active check: `to="/products"` is active whether the
|
|
41
|
-
* current location is `/en/products` or `/produkty` under a mapped locale.
|
|
42
|
-
*/
|
|
43
|
-
export declare function useIsLocalizedActive(canonicalTarget: string): () => boolean;
|
|
@@ -3,7 +3,7 @@ export { createI18nSsrHandoffInlineData, I18N_SSR_HANDOFF_ELEMENT_ID, type I18nS
|
|
|
3
3
|
export { type CreateI18nUrlRewriteOptions, createI18nUrlRewrite, type I18nLocationRewrite, } from '@modern-js/i18n-runtime-extensions/urlRewrite';
|
|
4
4
|
export { I18nContext } from './context.js';
|
|
5
5
|
export { I18nProvider, type I18nProviderProps } from './I18nProvider.js';
|
|
6
|
-
export { LocalizedLink, type LocalizedLinkProps
|
|
6
|
+
export { LocalizedLink, type LocalizedLinkProps } from './LocalizedLink.js';
|
|
7
7
|
export { createLatestLanguageSync, type SolidLatestLanguageSyncOptions, } from './languageSync.js';
|
|
8
8
|
export type { I18nContextValue, I18nInstanceLike } from './types.js';
|
|
9
9
|
export { type UseI18nReturn, useI18n } from './useI18n.js';
|
package/dist/types/index.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ export declare const SOLID_RENDERER_PROFILE: {
|
|
|
5
5
|
readonly web: '2.0.0-rc.15';
|
|
6
6
|
readonly router: '@modern-js/renderer-solid/router@3.8.3';
|
|
7
7
|
readonly routerSource: '4fbd65c5c3b40be87a6248867c22021c1c19bba8';
|
|
8
|
-
readonly routerCore: '1.171.
|
|
8
|
+
readonly routerCore: '1.171.38';
|
|
9
9
|
readonly history: '1.162.5';
|
|
10
10
|
readonly publicRouteData: {
|
|
11
11
|
readonly containers: readonly ['plain-record', 'null-record', 'array'];
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { JSX } from '@solidjs/web';
|
|
2
|
-
import type { AnyRouter, RegisteredRouter
|
|
2
|
+
import type { AnyRouter, RegisteredRouter } from '@tanstack/router-core';
|
|
3
|
+
import type { RouterOptions } from './routerOptions.js';
|
|
3
4
|
export declare function RouterContextProvider<TRouter extends AnyRouter = RegisteredRouter, TDehydrated extends Record<string, any> = Record<string, any>>({ router, children, ...rest }: RouterProps<TRouter, TDehydrated> & {
|
|
4
5
|
children: () => JSX.Element;
|
|
5
6
|
}): JSX.Element;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export type { BlockerFn, HistoryLocation, HistoryState, ParsedPath, RouterHistory, } from '@tanstack/history';
|
|
2
2
|
export { createBrowserHistory, createHashHistory, createHistory, createMemoryHistory, } from '@tanstack/history';
|
|
3
|
-
export type { AbsoluteToPath, ActiveOptions, AllContext, AllLoaderData, AllParams, AnyContext, AnyPathParams, AnyRedirect, AnyRoute, AnyRouteMatch, AnyRouter, AnyRouterWithContext, AnyRouteWithContext, AnySchema,
|
|
4
|
-
export { cleanPath, composeRewrites, createControlledPromise,
|
|
3
|
+
export type { AbsoluteToPath, ActiveOptions, AllContext, AllLoaderData, AllParams, AnyContext, AnyPathParams, AnyRedirect, AnyRoute, AnyRouteMatch, AnyRouter, AnyRouterWithContext, AnyRouteWithContext, AnySchema, AnyValidator, AnyValidatorAdapter, AnyValidatorFn, AnyValidatorObj, Assign, BaseRouteOptions, BeforeLoadContextOptions, BeforeLoadContextParameter, BuildLocationFn, BuildNextOptions, CommitLocationOptions, Constrain, ContextAsyncReturnType, ContextOptions, ContextReturnType, ControllablePromise, ControlledPromise, CreateFileRoute, CreateLazyFileRoute, DefaultValidator, DeferredPromise, DeferredPromiseState, ErrorComponentProps, ErrorRouteProps, Expand, FileBaseRouteOptions, FileRoutesByPath, FileRouteTypes, FullSearchSchema, FullSearchSchemaInput, FullSearchSchemaOption, InferAllContext, InferAllParams, InferDescendantToPaths, InferFrom, InferFullSearchSchema, InferFullSearchSchemaInput, InferMaskFrom, InferMaskTo, InferSelected, InferShouldThrow, InferStrict, InferTo, IntersectAssign, LazyRouteOptions, LinkOptions, ListenerFn, LoaderFnContext, LocationRewrite, LocationRewriteFunction, LooseAsyncReturnType, LooseReturnType, MakeOptionalPathParams, MakeRemountDepsOptionsUnion, MakeRouteMatch, MakeRouteMatchUnion, Manifest, MatchLocation, MatchRouteOptions, MergeAll, MetaDescriptor, NavigateFn, NavigateOptions, NotFoundError, NotFoundRouteProps, ParamsOptions, ParsedLocation, ParseParamsFn, ParseRoute, PathParamOptions, PreloadableObj, Redirect, RedirectOptions, Register, RegisteredRouter, RelativeToCurrentPath, RelativeToParentPath, RelativeToPath, RelativeToPathAutoComplete, RemountDepsOptions, RemoveLeadingSlashes, RemoveTrailingSlashes, ResolveAllContext, ResolveAllParamsFromParent, ResolvedRedirect, ResolveFullPath, ResolveFullSearchSchema, ResolveFullSearchSchemaInput, ResolveId, ResolveLoaderData, ResolveOptionalParams, ResolveParams, ResolveRelativePath, ResolveRequiredParams, ResolveRoute, ResolveRouteContext, ResolveSearchValidatorInput, ResolveSearchValidatorInputFn, ResolveValidatorInput, ResolveValidatorInputFn, ResolveValidatorOutput, ResolveValidatorOutputFn, RootRouteId, RootRouteOptions, RouteById, RouteByPath, RouteConstraints, RouteContext, RouteContextFn, RouteContextOptions, RouteContextParameter, RouteIds, RouteLinkEntry, RouteLoaderFn, RouteMask, RouteMatch, RouteOptions, RoutePathOptions, RoutePathOptionsIntersection, RoutePaths, RouterConstructorOptions, RouterContextOptions, RouterEvent, RouterEvents, RouterListener, RouterManagedTag, RouterState, RoutesById, RoutesByPath, SearchFilter, SearchParamOptions, SearchParser, SearchSchemaInput, SearchSerializer, SerializableExtensions, StaticDataRouteOption, StringifyParamsFn, ToMaskOptions, ToOptions, ToPathOption, ToSubOptions, TrailingSlashOption, TrimPath, TrimPathLeft, TrimPathRight, UpdatableRouteOptions, UpdatableStaticRouteOption, UseNavigateResult, ValidateFromPath, ValidateId, ValidateNavigateOptions, ValidateNavigateOptionsArray, ValidateParams, ValidateRedirectOptions, ValidateRedirectOptionsArray, ValidateSearch, ValidateToPath, ValidateUseParamsResult, ValidateUseSearchResult, Validator, ValidatorAdapter, ValidatorFn, ValidatorObj, } from '@tanstack/router-core';
|
|
4
|
+
export { cleanPath, composeRewrites, createControlledPromise, DEFAULT_PROTOCOL_ALLOWLIST, deepEqual, defaultParseSearch, defaultStringifySearch, defer, functionalUpdate, interpolatePath, isMatch, isNotFound, isRedirect, joinPaths, lazyFn, notFound, parseSearchWith, redirect, replaceEqualDeep, resolvePath, retainSearchParams, rootRouteId, SearchParamError, stringifySearchWith, stripSearchParams, trimPath, trimPathLeft, trimPathRight, } from '@tanstack/router-core';
|
|
5
5
|
export { Asset } from './Asset.js';
|
|
6
6
|
export type { AwaitOptions } from './awaited.js';
|
|
7
7
|
export { Await, useAwaited } from './awaited.js';
|
|
@@ -26,6 +26,7 @@ export { prepareRouterMatchTransfer } from './registryTransfer.js';
|
|
|
26
26
|
export type { AnyRootRoute, AsyncRouteComponent, ErrorRouteComponent, NotFoundRouteComponent, RouteComponent, SolidNode, } from './route.js';
|
|
27
27
|
export { createRootRoute, createRootRouteWithContext, createRoute, createRouteMask, getRouteApi, NotFoundRoute, RootRoute, Route, RouteApi, rootRouteWithContext, } from './route.js';
|
|
28
28
|
export { createRouter, Router } from './router.js';
|
|
29
|
+
export type { RouterOptions } from './routerOptions.js';
|
|
29
30
|
export { ScriptOnce } from './ScriptOnce.js';
|
|
30
31
|
export { Scripts } from './Scripts.js';
|
|
31
32
|
export { ScrollRestoration, useElementScrollRestoration, } from './ScrollRestoration.js';
|
|
@@ -26,6 +26,8 @@ import { type PublicSnapshotOwner } from './publicMatchData.js';
|
|
|
26
26
|
export declare const MATCH_KEY_PREFIX = "tsr:";
|
|
27
27
|
/** Bind the managed public-data scope before native router construction claims hydration. */
|
|
28
28
|
export declare function registerPublicRouteDataOwner(routeTree: object, owner: PublicSnapshotOwner, privateContexts?: readonly object[]): void;
|
|
29
|
+
/** Managed match transfer uses the immutable public-data contract, not custom adapters. */
|
|
30
|
+
export declare function assertNativeSerializationOptions(options: object): void;
|
|
29
31
|
/** Framework SSR accepts only routers that passed its public-data factory boundary. */
|
|
30
32
|
export declare function requireManagedPublicRouter(router: AnyRouter): void;
|
|
31
33
|
/** Project composed public context before every native match-store publication. */
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { RouterHistory } from '@tanstack/history';
|
|
2
|
+
import type { AnyRoute, RouterOptions as CoreRouterOptions, TrailingSlashOption } from '@tanstack/router-core';
|
|
3
|
+
/** Native managed hydration transfers immutable public data only. */
|
|
4
|
+
export type RouterOptions<TRouteTree extends AnyRoute, TTrailingSlashOption extends TrailingSlashOption, TDefaultStructuralSharingOption extends boolean = false, TRouterHistory extends RouterHistory = RouterHistory, TDehydrated = undefined> = Omit<CoreRouterOptions<TRouteTree, TTrailingSlashOption, TDefaultStructuralSharingOption, TRouterHistory, TDehydrated>, 'serializationAdapters'> & {
|
|
5
|
+
serializationAdapters?: never;
|
|
6
|
+
};
|
package/dist/types/router.d.ts
CHANGED
|
@@ -14,11 +14,11 @@ export interface FileSystemRouteModule {
|
|
|
14
14
|
pendingComponent?: RouteComponent;
|
|
15
15
|
errorComponent?: ErrorRouteComponent;
|
|
16
16
|
notFoundComponent?: NotFoundRouteComponent;
|
|
17
|
-
head?: AnyRoute['options']['head']
|
|
17
|
+
head?: NonNullable<AnyRoute['options']['head']>;
|
|
18
18
|
/** Explicit returned context becomes immutable public native route context. */
|
|
19
|
-
beforeLoad?: AnyRoute['options']['beforeLoad']
|
|
19
|
+
beforeLoad?: NonNullable<AnyRoute['options']['beforeLoad']>;
|
|
20
20
|
/** Native synchronous context contributions are checked before composition. */
|
|
21
|
-
context?: AnyRoute['options']['context']
|
|
21
|
+
context?: NonNullable<AnyRoute['options']['context']>;
|
|
22
22
|
validateSearch?: (search: Record<string, unknown>) => Record<string, unknown>;
|
|
23
23
|
}
|
|
24
24
|
/** Resolve location through the native router before committing HTTP headers. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bleedingdev/modern-js-renderer-solid",
|
|
3
|
-
"version": "3.9.3-ultramodern.
|
|
3
|
+
"version": "3.9.3-ultramodern.9",
|
|
4
4
|
"description": "UltraModern native Solid 2 application runtime.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -68,18 +68,18 @@
|
|
|
68
68
|
"dependencies": {
|
|
69
69
|
"@babel/core": "7.29.7",
|
|
70
70
|
"@jridgewell/remapping": "2.3.5",
|
|
71
|
-
"@modern-js/renderer-core": "npm:@bleedingdev/modern-js-renderer-core@3.9.3-ultramodern.
|
|
71
|
+
"@modern-js/renderer-core": "npm:@bleedingdev/modern-js-renderer-core@3.9.3-ultramodern.9",
|
|
72
72
|
"@solidjs/signals": "2.0.0-rc.15",
|
|
73
73
|
"@solidjs/web": "2.0.0-rc.15",
|
|
74
74
|
"@tanstack/history": "1.162.5",
|
|
75
|
-
"@tanstack/router-core": "1.171.37",
|
|
76
75
|
"isbot": "5.2.2",
|
|
77
76
|
"solid-js": "2.0.0-rc.15"
|
|
78
77
|
},
|
|
79
78
|
"peerDependencies": {
|
|
80
|
-
"@modern-js/i18n-runtime-extensions": "3.9.3-ultramodern.
|
|
79
|
+
"@modern-js/i18n-runtime-extensions": "3.9.3-ultramodern.9",
|
|
81
80
|
"@rsbuild/core": "2.2.13",
|
|
82
81
|
"@solidjs/compiler": "2.0.0-rc.15",
|
|
82
|
+
"@tanstack/router-core": "1.171.38",
|
|
83
83
|
"i18next": ">=25.7.4"
|
|
84
84
|
},
|
|
85
85
|
"peerDependenciesMeta": {
|
|
@@ -97,27 +97,36 @@
|
|
|
97
97
|
}
|
|
98
98
|
},
|
|
99
99
|
"devDependencies": {
|
|
100
|
-
"@modern-js/i18n-runtime-extensions": "npm:@bleedingdev/modern-js-i18n-runtime-extensions@3.9.3-ultramodern.
|
|
101
|
-
"@modern-js/tsconfig": "npm:@bleedingdev/modern-js-tsconfig@3.9.3-ultramodern.
|
|
100
|
+
"@modern-js/i18n-runtime-extensions": "npm:@bleedingdev/modern-js-i18n-runtime-extensions@3.9.3-ultramodern.9",
|
|
101
|
+
"@modern-js/tsconfig": "npm:@bleedingdev/modern-js-tsconfig@3.9.3-ultramodern.9",
|
|
102
102
|
"@rsbuild/core": "2.2.13",
|
|
103
103
|
"@rslib/core": "1.0.3",
|
|
104
104
|
"@scripts/rstest-config": "2.66.0",
|
|
105
105
|
"@solidjs/compiler": "2.0.0-rc.15",
|
|
106
|
+
"@tanstack/router-core": "https://github.com/BleedingDev/router/releases/download/ultramodern-router-core-585d3233f7f5/tanstack-router-core-1.171.38.tgz",
|
|
106
107
|
"@types/node": "^26.6.5"
|
|
107
108
|
},
|
|
108
109
|
"sideEffects": false,
|
|
109
110
|
"publishConfig": {
|
|
110
111
|
"access": "public"
|
|
111
112
|
},
|
|
113
|
+
"ultramodern": {
|
|
114
|
+
"nativeArtifacts": {
|
|
115
|
+
"@tanstack/router-core": {
|
|
116
|
+
"request": "https://github.com/BleedingDev/router/releases/download/ultramodern-router-core-585d3233f7f5/tanstack-router-core-1.171.38.tgz",
|
|
117
|
+
"version": "1.171.38",
|
|
118
|
+
"integrity": "sha512-d/aGvcyEVwDh2Cn9MuZM1n1dqTIqkFYi7AHY2Na/6x/a0jVSDVTNVVVY7QEsuKFXktaJF9zjVwetaRm/0MALZg=="
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
"sourceName": "@modern-js/renderer-solid"
|
|
122
|
+
},
|
|
112
123
|
"scripts": {
|
|
113
124
|
"build": "rslib build",
|
|
114
|
-
"test": "rstest"
|
|
125
|
+
"test": "rstest",
|
|
126
|
+
"test:types": "tsc -p tests/types/tsconfig.json"
|
|
115
127
|
},
|
|
116
128
|
"homepage": "https://github.com/ultramodernjs/ultramodern.js#readme",
|
|
117
129
|
"bugs": {
|
|
118
130
|
"url": "https://github.com/ultramodernjs/ultramodern.js/issues"
|
|
119
|
-
},
|
|
120
|
-
"ultramodern": {
|
|
121
|
-
"sourceName": "@modern-js/renderer-solid"
|
|
122
131
|
}
|
|
123
132
|
}
|
package/src/actions.tsx
CHANGED
|
@@ -165,7 +165,11 @@ export function createRouteAction(options: RouteActionOptions): RouteAction {
|
|
|
165
165
|
assertRendererIdentity(options.rendererIdentity, configuredIdentity);
|
|
166
166
|
}
|
|
167
167
|
const identity = Object.freeze({ ...options.rendererIdentity });
|
|
168
|
-
const client = createDataClient(
|
|
168
|
+
const client = createDataClient(
|
|
169
|
+
routeId,
|
|
170
|
+
identity,
|
|
171
|
+
options.fetch ? { fetch: options.fetch } : {},
|
|
172
|
+
);
|
|
169
173
|
// Capture the request while the native SSR request scope is active.
|
|
170
174
|
const requestURL = getRequestEvent()?.request.url;
|
|
171
175
|
const url = () => {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { DataProtocolError } from '@modern-js/renderer-core/data';
|
|
2
|
-
import
|
|
2
|
+
import type { RequestSession } from '@modern-js/renderer-core/session';
|
|
3
|
+
import { getRequestEvent, type JSX, useHead } from '@solidjs/web';
|
|
3
4
|
import type { AnyRouter, RegisteredRouter } from '@tanstack/router-core';
|
|
4
5
|
import { ownApplicationRouteData } from './route-completion';
|
|
5
6
|
import { toHeadTags, useTags } from './router-binding/headContentUtils';
|
|
@@ -22,10 +23,15 @@ export function ApplicationRouter<
|
|
|
22
23
|
TRouter extends AnyRouter = RegisteredRouter,
|
|
23
24
|
TDehydrated extends Record<string, unknown> = Record<string, unknown>,
|
|
24
25
|
>(props: Omit<RouterProps<TRouter, TDehydrated>, 'context'>): JSX.Element {
|
|
25
|
-
if (Object.hasOwn(props, 'context'))
|
|
26
|
-
|
|
26
|
+
if (Object.hasOwn(props, 'context')) {
|
|
27
|
+
const error = new DataProtocolError(
|
|
27
28
|
'Set public Solid router context in createApplicationRouter; ApplicationRouter context overrides are unsupported',
|
|
28
29
|
);
|
|
30
|
+
const session: RequestSession | undefined =
|
|
31
|
+
getRequestEvent()?.locals.session;
|
|
32
|
+
session?.fail(error);
|
|
33
|
+
throw error;
|
|
34
|
+
}
|
|
29
35
|
const { router, ...rest } = props;
|
|
30
36
|
ownApplicationRouteData(router);
|
|
31
37
|
return (
|
package/src/entry-client.tsx
CHANGED
|
@@ -27,6 +27,7 @@ export function startNativeClient(options: SolidClientEntryOptions): void {
|
|
|
27
27
|
readBootstrap: readSolidDocumentBootstrap,
|
|
28
28
|
createRouter: (application, routerOptions) =>
|
|
29
29
|
createNativeRouter(application, routerOptions, solidRouterFactory),
|
|
30
|
+
disposeRouter: router => router.history.destroy(),
|
|
30
31
|
async prepareRouter(router, hydrating) {
|
|
31
32
|
// Hydration adopts the server's matches; a fresh mount loads its own.
|
|
32
33
|
if (!hydrating) await router.load();
|
package/src/federation-ssr.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { FederatedAssets } from '@modern-js/renderer-core/federation';
|
|
2
2
|
import type { SolidFederationScope } from './federation-context';
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -10,17 +10,39 @@ import type { SolidFederationScope } from './federation-context';
|
|
|
10
10
|
/** Asset keys for server-rendered remotes, e.g. `ultramodern-federation:remote/Widget`. */
|
|
11
11
|
export const FEDERATED_ASSET_PREFIX = 'ultramodern-federation:';
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
interface CapturedServerAssets {
|
|
14
|
+
readonly hydration: string;
|
|
15
|
+
readonly assets: FederatedAssets;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const capturedAssets = new WeakMap<
|
|
19
|
+
SolidFederationScope,
|
|
20
|
+
Map<string, CapturedServerAssets>
|
|
21
|
+
>();
|
|
22
|
+
|
|
23
|
+
/** Retain the assets admitted with this module until its response resolves them. */
|
|
24
|
+
export function captureFederatedServerAssets(
|
|
25
|
+
scope: SolidFederationScope,
|
|
16
26
|
id: string,
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
const
|
|
20
|
-
if (!assets || !scope
|
|
27
|
+
assets: FederatedAssets | undefined,
|
|
28
|
+
): string | undefined {
|
|
29
|
+
const instance = scope.instance;
|
|
30
|
+
if (!assets || !scope.hydrationModule || !instance?.name) return undefined;
|
|
21
31
|
// The browser module finds this host by name among the realm's instances.
|
|
22
32
|
const hydration = `${scope.hydrationModule}?id=${encodeURIComponent(id)}&host=${encodeURIComponent(instance.name)}`;
|
|
23
|
-
|
|
33
|
+
let records = capturedAssets.get(scope);
|
|
34
|
+
if (!records) {
|
|
35
|
+
records = new Map();
|
|
36
|
+
capturedAssets.set(scope, records);
|
|
37
|
+
}
|
|
38
|
+
const baseKey = federatedAssetKey(id);
|
|
39
|
+
let key = baseKey;
|
|
40
|
+
// Two loads of one remote may straddle a development revision in one response.
|
|
41
|
+
// Give each loaded module its own key rather than replacing earlier assets.
|
|
42
|
+
for (let revision = 1; records.has(key); revision += 1)
|
|
43
|
+
key = `${baseKey}:${revision}`;
|
|
44
|
+
records.set(key, { hydration, assets });
|
|
45
|
+
return key;
|
|
24
46
|
}
|
|
25
47
|
|
|
26
48
|
export function federatedAssetKey(id: string): string {
|
|
@@ -103,10 +125,7 @@ export function withFederatedAssets<Manifest>(
|
|
|
103
125
|
const resolve = (key: string): ResolvedDocumentAssets | null => {
|
|
104
126
|
if (!key.startsWith(FEDERATED_ASSET_PREFIX))
|
|
105
127
|
return resolveStaticAssets(modules, key);
|
|
106
|
-
const remote =
|
|
107
|
-
scope,
|
|
108
|
-
key.slice(FEDERATED_ASSET_PREFIX.length),
|
|
109
|
-
);
|
|
128
|
+
const remote = capturedAssets.get(scope)?.get(key);
|
|
110
129
|
if (!remote) return null;
|
|
111
130
|
return {
|
|
112
131
|
js: [joinAssetPath(modules._base, remote.hydration), ...remote.assets.js],
|
package/src/federation.tsx
CHANGED
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
FederationContext,
|
|
17
17
|
type SolidFederationScope,
|
|
18
18
|
} from './federation-context';
|
|
19
|
-
import {
|
|
19
|
+
import { captureFederatedServerAssets } from './federation-ssr';
|
|
20
20
|
|
|
21
21
|
/** The module shape a same-renderer Module Federation remote exposes. */
|
|
22
22
|
export interface FederatedModule<P extends object> {
|
|
@@ -120,11 +120,14 @@ export function federatedComponent<P extends object = Record<string, never>>(
|
|
|
120
120
|
throw new Error(
|
|
121
121
|
'A federatedComponent loader renders in the browser only; use a remote id to server-render it',
|
|
122
122
|
);
|
|
123
|
-
const module = await withTimeout(
|
|
123
|
+
const { module, assets } = await withTimeout(
|
|
124
124
|
Promise.resolve().then(() =>
|
|
125
125
|
id
|
|
126
126
|
? loadFederatedModule<FederatedModule<P>>(scope.instance, id)
|
|
127
|
-
: (remote as () => Promise<
|
|
127
|
+
: (remote as () => Promise<FederatedModule<P>>)().then(module => ({
|
|
128
|
+
module,
|
|
129
|
+
assets: undefined,
|
|
130
|
+
})),
|
|
128
131
|
),
|
|
129
132
|
timeout,
|
|
130
133
|
id ?? 'loader',
|
|
@@ -135,13 +138,14 @@ export function federatedComponent<P extends object = Record<string, never>>(
|
|
|
135
138
|
'A federated Solid module must default-export a component',
|
|
136
139
|
);
|
|
137
140
|
if (!isServer || !id) return { default: component };
|
|
138
|
-
|
|
141
|
+
const assetKey = captureFederatedServerAssets(scope, id, assets);
|
|
142
|
+
if (!assetKey)
|
|
139
143
|
throw new Error(
|
|
140
144
|
`Cannot server-render ${id}: the host has no server federation state for its browser assets`,
|
|
141
145
|
);
|
|
142
146
|
// Solid resolves a loaded module's $$moduleUrl through the response's
|
|
143
147
|
// asset resolver: stylesheets, preloads and the hydration module.
|
|
144
|
-
return { default: component, $$moduleUrl:
|
|
148
|
+
return { default: component, $$moduleUrl: assetKey };
|
|
145
149
|
}) as Component<P>;
|
|
146
150
|
// Solid's lazy caches its module. Scope the cache to one application or
|
|
147
151
|
// response, so two hosts rendering this component never share a remote.
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
|
-
canonicalPath,
|
|
3
2
|
configuredLanguage,
|
|
4
3
|
localizePath,
|
|
5
4
|
} from '@modern-js/i18n-runtime-extensions/paths';
|
|
6
5
|
import type { JSX } from '@solidjs/web';
|
|
7
6
|
import * as Solid from 'solid-js';
|
|
8
7
|
import {
|
|
8
|
+
type AnyRouter,
|
|
9
9
|
Link,
|
|
10
10
|
type LinkComponentProps,
|
|
11
11
|
useLocation,
|
|
@@ -13,110 +13,71 @@ import {
|
|
|
13
13
|
} from '../router';
|
|
14
14
|
import { I18nContext } from './context';
|
|
15
15
|
|
|
16
|
-
export interface LocalizedLinkProps
|
|
16
|
+
export interface LocalizedLinkProps
|
|
17
|
+
extends Omit<
|
|
18
|
+
LinkComponentProps<'a', AnyRouter, string, string>,
|
|
19
|
+
'to' | 'mask' | 'href' | 'publicHref' | '_fromLocation'
|
|
20
|
+
> {
|
|
17
21
|
/** A canonical, language-agnostic target, e.g. `/products/red-shoe`. */
|
|
18
22
|
to: string;
|
|
19
23
|
/** Localize for a specific language instead of the current one. */
|
|
20
24
|
language?: string;
|
|
21
|
-
children?: LinkComponentProps<'a'>['children'];
|
|
22
|
-
class?: string;
|
|
23
|
-
activeProps?: LinkComponentProps<'a'>['activeProps'];
|
|
24
|
-
inactiveProps?: LinkComponentProps<'a'>['inactiveProps'];
|
|
25
|
-
activeOptions?: LinkComponentProps<'a'>['activeOptions'];
|
|
26
|
-
replace?: boolean;
|
|
27
|
-
preload?: LinkComponentProps<'a'>['preload'];
|
|
28
25
|
[key: `data-${string}`]: unknown;
|
|
29
26
|
}
|
|
30
27
|
|
|
31
28
|
/**
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
* `Link`. It resolves the href itself via the shared pathname helpers, so it
|
|
36
|
-
* works with or without the router's i18n `rewrite`. Every link, including
|
|
37
|
-
* one to another language, is the native `Link`: navigation, preloading and
|
|
38
|
-
* active state stay with the router.
|
|
39
|
-
*
|
|
40
|
-
* Under the i18n `rewrite` the router matches canonical paths and localizes
|
|
41
|
-
* outgoing locations to the current language, so a link to another language
|
|
42
|
-
* routes to the canonical `to` and publishes the target language's URL as a
|
|
43
|
-
* route mask (the rewrite keeps a mask's explicit language). The entry's
|
|
44
|
-
* router language synchronization (`syncWithRouter`) switches the i18next
|
|
45
|
-
* instance from the navigated URL, as it does for history navigation.
|
|
46
|
-
*
|
|
47
|
-
* Props are forwarded explicitly (not via object-rest or a missing
|
|
48
|
-
* `splitProps`/`mergeProps` primitive — Solid 2 only exports `merge`) so
|
|
49
|
-
* each one is read directly from `props` in JSX position and keeps its
|
|
50
|
-
* per-key reactivity.
|
|
29
|
+
* The native router owns navigation, preloading and active state. A route mask
|
|
30
|
+
* publishes the selected language URL while keeping the canonical route;
|
|
31
|
+
* the entry's router subscription synchronizes the i18next instance.
|
|
51
32
|
*/
|
|
52
33
|
export function LocalizedLink(props: LocalizedLinkProps): JSX.Element {
|
|
53
34
|
const context = Solid.useContext(I18nContext);
|
|
54
35
|
const router = useRouter();
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
// An explicit language must be configured: it becomes the locale prefix.
|
|
36
|
+
const location = useLocation();
|
|
37
|
+
const options = Solid.omit(props, 'to', 'language', 'children');
|
|
58
38
|
const targetLanguage = () =>
|
|
59
39
|
props.language === undefined
|
|
60
40
|
? context.language()
|
|
61
41
|
: configuredLanguage(props.language, context.languages);
|
|
62
|
-
const
|
|
63
|
-
|
|
42
|
+
const externalTarget = () =>
|
|
43
|
+
props.to.startsWith('//') || URL.canParse(props.to);
|
|
44
|
+
const canonicalTarget = () => ({
|
|
45
|
+
to: props.to,
|
|
46
|
+
from: props.from,
|
|
47
|
+
params: props.params,
|
|
48
|
+
unsafeRelative: props.unsafeRelative,
|
|
49
|
+
});
|
|
50
|
+
const pathname = Solid.createMemo(() => {
|
|
51
|
+
if (externalTarget()) return props.to;
|
|
52
|
+
const target = router.buildLocation({
|
|
53
|
+
...canonicalTarget(),
|
|
54
|
+
_fromLocation: location(),
|
|
55
|
+
});
|
|
56
|
+
return localizePath(target.pathname, targetLanguage(), {
|
|
64
57
|
languages: [...context.languages],
|
|
65
58
|
localisedUrls: context.localisedUrls,
|
|
66
|
-
})
|
|
67
|
-
);
|
|
68
|
-
const crossLanguage = () => targetLanguage() !== context.language();
|
|
69
|
-
const masked = () => rewrite && crossLanguage();
|
|
70
|
-
// Declared `data-*` attributes reach the anchor through getters, so each
|
|
71
|
-
// keeps its own reactivity like the explicitly forwarded props.
|
|
72
|
-
const dataAttributes = Object.defineProperties(
|
|
73
|
-
{},
|
|
74
|
-
Object.fromEntries(
|
|
75
|
-
Object.keys(props)
|
|
76
|
-
.filter(key => key.startsWith('data-'))
|
|
77
|
-
.map(key => [
|
|
78
|
-
key,
|
|
79
|
-
{
|
|
80
|
-
enumerable: true,
|
|
81
|
-
get: () => props[key as `data-${string}`],
|
|
82
|
-
},
|
|
83
|
-
]),
|
|
84
|
-
),
|
|
85
|
-
);
|
|
59
|
+
});
|
|
60
|
+
});
|
|
86
61
|
|
|
87
62
|
return (
|
|
88
|
-
<Link
|
|
89
|
-
{...
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
63
|
+
<Link<AnyRouter, string, string, string, string>
|
|
64
|
+
{...options}
|
|
65
|
+
to={router.options.rewrite ? props.to : pathname()}
|
|
66
|
+
mask={
|
|
67
|
+
router.options.rewrite && !externalTarget()
|
|
68
|
+
? {
|
|
69
|
+
to: pathname(),
|
|
70
|
+
inheritSearch: true,
|
|
71
|
+
hash: props.hash,
|
|
72
|
+
state: props.state,
|
|
73
|
+
}
|
|
74
|
+
: undefined
|
|
75
|
+
}
|
|
76
|
+
hreflang={
|
|
77
|
+
targetLanguage() === context.language() ? undefined : targetLanguage()
|
|
78
|
+
}
|
|
104
79
|
>
|
|
105
80
|
{props.children}
|
|
106
81
|
</Link>
|
|
107
82
|
);
|
|
108
83
|
}
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* Language-invariant active check: `to="/products"` is active whether the
|
|
112
|
-
* current location is `/en/products` or `/produkty` under a mapped locale.
|
|
113
|
-
*/
|
|
114
|
-
export function useIsLocalizedActive(canonicalTarget: string): () => boolean {
|
|
115
|
-
const context = Solid.useContext(I18nContext);
|
|
116
|
-
const location = useLocation();
|
|
117
|
-
return () =>
|
|
118
|
-
canonicalPath(location().pathname, {
|
|
119
|
-
languages: [...context.languages],
|
|
120
|
-
localisedUrls: context.localisedUrls,
|
|
121
|
-
}) === canonicalTarget;
|
|
122
|
-
}
|
package/src/i18n/index.ts
CHANGED
|
@@ -22,11 +22,7 @@ export {
|
|
|
22
22
|
} from '@modern-js/i18n-runtime-extensions/urlRewrite';
|
|
23
23
|
export { I18nContext } from './context';
|
|
24
24
|
export { I18nProvider, type I18nProviderProps } from './I18nProvider';
|
|
25
|
-
export {
|
|
26
|
-
LocalizedLink,
|
|
27
|
-
type LocalizedLinkProps,
|
|
28
|
-
useIsLocalizedActive,
|
|
29
|
-
} from './LocalizedLink';
|
|
25
|
+
export { LocalizedLink, type LocalizedLinkProps } from './LocalizedLink';
|
|
30
26
|
export {
|
|
31
27
|
createLatestLanguageSync,
|
|
32
28
|
type SolidLatestLanguageSyncOptions,
|
package/src/index.ts
CHANGED
|
@@ -5,7 +5,7 @@ export const SOLID_RENDERER_PROFILE = {
|
|
|
5
5
|
web: '2.0.0-rc.15',
|
|
6
6
|
router: '@modern-js/renderer-solid/router@3.8.3',
|
|
7
7
|
routerSource: '4fbd65c5c3b40be87a6248867c22021c1c19bba8',
|
|
8
|
-
routerCore: '1.171.
|
|
8
|
+
routerCore: '1.171.38',
|
|
9
9
|
history: '1.162.5',
|
|
10
10
|
publicRouteData: {
|
|
11
11
|
containers: ['plain-record', 'null-record', 'array'],
|
package/src/plugin/profile.ts
CHANGED
|
@@ -18,11 +18,13 @@ export const solidProfile: RendererBuildProfile<'solid'> = {
|
|
|
18
18
|
name: '@modern-js/renderer-solid',
|
|
19
19
|
version: '3.8.3',
|
|
20
20
|
coreName: '@tanstack/router-core',
|
|
21
|
-
coreVersion: '1.171.
|
|
21
|
+
coreVersion: '1.171.38',
|
|
22
22
|
},
|
|
23
23
|
sourceExtensions: ['.tsx', '.ts', '.jsx', '.js'],
|
|
24
24
|
jsxImportSource: '@solidjs/web',
|
|
25
25
|
dependencies: {
|
|
26
|
+
'@tanstack/router-core':
|
|
27
|
+
'https://github.com/BleedingDev/router/releases/download/ultramodern-router-core-585d3233f7f5/tanstack-router-core-1.171.38.tgz',
|
|
26
28
|
'solid-js': '2.0.0-rc.15',
|
|
27
29
|
'@solidjs/web': '2.0.0-rc.15',
|
|
28
30
|
'@solidjs/signals': '2.0.0-rc.15',
|
package/src/route-completion.tsx
CHANGED
|
@@ -10,7 +10,10 @@ import {
|
|
|
10
10
|
preparePublicContextData,
|
|
11
11
|
useMatch,
|
|
12
12
|
} from './router-binding/index';
|
|
13
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
assertNativeSerializationOptions,
|
|
15
|
+
registerPublicRouteDataOwner,
|
|
16
|
+
} from './router-binding/registryTransfer';
|
|
14
17
|
|
|
15
18
|
interface CompletionRecord {
|
|
16
19
|
readonly failure: Accessor<{ reason: unknown } | undefined>;
|
|
@@ -52,6 +55,7 @@ export function registerRouteCompletionScope(
|
|
|
52
55
|
}
|
|
53
56
|
|
|
54
57
|
export const createApplicationRouter: CreateRouterFn = options => {
|
|
58
|
+
assertNativeSerializationOptions(options);
|
|
55
59
|
const scope = options.routeTree
|
|
56
60
|
? routeScopes.get(options.routeTree)
|
|
57
61
|
: undefined;
|