akanjs 2.2.2 → 2.2.3-rc.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.
- package/fetch/client/fetchClient.ts +7 -2
- package/fetch/client/httpClient.ts +8 -4
- package/package.json +1 -1
- package/types/fetch/client/httpClient.d.ts +1 -0
- package/types/ui/System/Common.d.ts +6 -0
- package/types/ui/System/SSR.d.ts +2 -2
- package/ui/System/Common.tsx +6 -0
- package/ui/System/SSR.tsx +13 -6
|
@@ -142,8 +142,12 @@ export class FetchClient {
|
|
|
142
142
|
const argMap = new Map(serializerMap.entries().map(([key, serializer], idx) => [key, serializer(args[idx])]));
|
|
143
143
|
const url = FetchClient.makeHttpUrl(key, endpoint, prefix, argMap);
|
|
144
144
|
const headers = this.#makeAuthHeaders(option);
|
|
145
|
-
const
|
|
146
|
-
|
|
145
|
+
const baseUrl = option?.url;
|
|
146
|
+
|
|
147
|
+
const requestQuery = () => this.http.get(url, { headers, baseUrl });
|
|
148
|
+
const response = baseUrl
|
|
149
|
+
? await requestQuery()
|
|
150
|
+
: await memoizeRequestQuery(FetchClient.#makeRequestQueryCacheKey(this.origin, url, headers), requestQuery);
|
|
147
151
|
const parsedReturn = parseReturn(FetchClient.#deepCopy(response), { crystalize: option?.crystalize ?? true });
|
|
148
152
|
return parsedReturn;
|
|
149
153
|
};
|
|
@@ -158,6 +162,7 @@ export class FetchClient {
|
|
|
158
162
|
const body = HttpClient.makeBody(bodyArgs, uploadArgs, argMap);
|
|
159
163
|
const response = await this.http.post(url, body, {
|
|
160
164
|
headers: this.#makeAuthHeaders(option),
|
|
165
|
+
baseUrl: option?.url,
|
|
161
166
|
});
|
|
162
167
|
const parsedReturn = parseReturn(response, { crystalize: option?.crystalize ?? true });
|
|
163
168
|
return parsedReturn;
|
|
@@ -24,6 +24,7 @@ export interface ErrorConstructor {
|
|
|
24
24
|
|
|
25
25
|
interface FetchOptions {
|
|
26
26
|
headers?: Record<string, string>;
|
|
27
|
+
baseUrl?: string;
|
|
27
28
|
}
|
|
28
29
|
|
|
29
30
|
export class HttpClient {
|
|
@@ -38,8 +39,11 @@ export class HttpClient {
|
|
|
38
39
|
setErrorConstructor(ErrorCls?: ErrorConstructor) {
|
|
39
40
|
this.ErrorCls = ErrorCls;
|
|
40
41
|
}
|
|
42
|
+
#resolveBaseUrl(baseUrl?: string) {
|
|
43
|
+
return (baseUrl ?? this.baseUrl).replace(/\/$/, "");
|
|
44
|
+
}
|
|
41
45
|
async get<Returns = unknown>(url: string, options: FetchOptions = {}): Promise<Returns> {
|
|
42
|
-
const res = await fetch(`${this.baseUrl}${url}`, {
|
|
46
|
+
const res = await fetch(`${this.#resolveBaseUrl(options.baseUrl)}${url}`, {
|
|
43
47
|
headers: { "Content-Type": "application/json", ...options.headers },
|
|
44
48
|
});
|
|
45
49
|
return await this.#readJsonResponse<Returns>(res);
|
|
@@ -55,7 +59,7 @@ export class HttpClient {
|
|
|
55
59
|
options: FetchOptions = {},
|
|
56
60
|
): Promise<Returns> {
|
|
57
61
|
const { body, headers } = this.#makeReqContent(data);
|
|
58
|
-
const res = await fetch(`${this.baseUrl}${url}`, {
|
|
62
|
+
const res = await fetch(`${this.#resolveBaseUrl(options.baseUrl)}${url}`, {
|
|
59
63
|
method: "PUT",
|
|
60
64
|
body,
|
|
61
65
|
headers: { ...headers, ...options.headers },
|
|
@@ -68,7 +72,7 @@ export class HttpClient {
|
|
|
68
72
|
options: FetchOptions = {},
|
|
69
73
|
): Promise<Returns> {
|
|
70
74
|
const { body, headers } = this.#makeReqContent(data);
|
|
71
|
-
const res = await fetch(`${this.baseUrl}${url}`, {
|
|
75
|
+
const res = await fetch(`${this.#resolveBaseUrl(options.baseUrl)}${url}`, {
|
|
72
76
|
method: "POST",
|
|
73
77
|
body,
|
|
74
78
|
headers: { ...headers, ...options.headers },
|
|
@@ -76,7 +80,7 @@ export class HttpClient {
|
|
|
76
80
|
return await this.#readJsonResponse<Returns>(res);
|
|
77
81
|
}
|
|
78
82
|
async delete<Returns = unknown>(url: string, options: FetchOptions = {}): Promise<Returns> {
|
|
79
|
-
const res = await fetch(`${this.baseUrl}${url}`, {
|
|
83
|
+
const res = await fetch(`${this.#resolveBaseUrl(options.baseUrl)}${url}`, {
|
|
80
84
|
method: "DELETE",
|
|
81
85
|
headers: { "Content-Type": "application/json", ...options.headers },
|
|
82
86
|
});
|
package/package.json
CHANGED
|
@@ -30,6 +30,12 @@ export interface ProviderProps {
|
|
|
30
30
|
reconnect?: boolean;
|
|
31
31
|
/** Active-locale dictionary injected by the server (SSR only) to seed the client Translator. */
|
|
32
32
|
dictionary?: Record<string, Record<string, unknown>>;
|
|
33
|
+
/**
|
|
34
|
+
* Full lang-keyed dictionary snapshot (SSR server-only). The provider seeds every locale into
|
|
35
|
+
* the RSC-worker Translator (free on the server, never shipped to the browser) and serializes only
|
|
36
|
+
* the request's active locale to the client, so translations resolve regardless of locale routing.
|
|
37
|
+
*/
|
|
38
|
+
allDictionary?: Record<string, Record<string, Record<string, unknown>>>;
|
|
33
39
|
/** Root route component used by CSR page loading. */
|
|
34
40
|
of: (props: unknown) => ReactNode | null;
|
|
35
41
|
}
|
package/types/ui/System/SSR.d.ts
CHANGED
|
@@ -3,13 +3,13 @@ import { type ReactNode } from "react";
|
|
|
3
3
|
import { type ProviderProps } from "./Common.d.ts";
|
|
4
4
|
export declare const SSR: {
|
|
5
5
|
(): import("react/jsx-runtime").JSX.Element;
|
|
6
|
-
Provider: ({ className, appName, params, head, manifest, env, gaTrackingId, children, theme, prefix, fonts, layoutStyle, reconnect, dictionary, of, }: SSRProviderProps) => import("react/jsx-runtime").JSX.Element;
|
|
6
|
+
Provider: ({ className, appName, params, head, manifest, env, gaTrackingId, children, theme, prefix, fonts, layoutStyle, reconnect, dictionary, allDictionary, of, }: SSRProviderProps) => import("react/jsx-runtime").JSX.Element;
|
|
7
7
|
Wrapper: ({ children, head, manifest, fonts, className, prefix, layoutStyle, }: SSRWrapperProps) => import("react/jsx-runtime").JSX.Element;
|
|
8
8
|
};
|
|
9
9
|
export type SSRProviderProps = ProviderProps & {
|
|
10
10
|
fonts?: ReactFont[];
|
|
11
11
|
};
|
|
12
|
-
declare const SSRProvider: ({ className, appName, params, head, manifest, env, gaTrackingId, children, theme, prefix, fonts, layoutStyle, reconnect, dictionary, of, }: SSRProviderProps) => import("react/jsx-runtime").JSX.Element;
|
|
12
|
+
declare const SSRProvider: ({ className, appName, params, head, manifest, env, gaTrackingId, children, theme, prefix, fonts, layoutStyle, reconnect, dictionary, allDictionary, of, }: SSRProviderProps) => import("react/jsx-runtime").JSX.Element;
|
|
13
13
|
interface SSRWrapperProps {
|
|
14
14
|
className?: string;
|
|
15
15
|
appName: string;
|
package/ui/System/Common.tsx
CHANGED
|
@@ -32,6 +32,12 @@ export interface ProviderProps {
|
|
|
32
32
|
reconnect?: boolean;
|
|
33
33
|
/** Active-locale dictionary injected by the server (SSR only) to seed the client Translator. */
|
|
34
34
|
dictionary?: Record<string, Record<string, unknown>>;
|
|
35
|
+
/**
|
|
36
|
+
* Full lang-keyed dictionary snapshot (SSR server-only). The provider seeds every locale into
|
|
37
|
+
* the RSC-worker Translator (free on the server, never shipped to the browser) and serializes only
|
|
38
|
+
* the request's active locale to the client, so translations resolve regardless of locale routing.
|
|
39
|
+
*/
|
|
40
|
+
allDictionary?: Record<string, Record<string, Record<string, unknown>>>;
|
|
35
41
|
/** Root route component used by CSR page loading. */
|
|
36
42
|
of: (props: unknown) => ReactNode | null;
|
|
37
43
|
}
|
package/ui/System/SSR.tsx
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { getEnv } from "akanjs/base";
|
|
2
|
-
import { clsx, type ReactFont, router, Translator, type WebAppManifest } from "akanjs/client";
|
|
2
|
+
import { clsx, type ReactFont, router, Translator, usePage, type WebAppManifest } from "akanjs/client";
|
|
3
3
|
import { setRequestTheme } from "akanjs/fetch";
|
|
4
4
|
import { Children, Fragment, type ReactNode, Suspense } from "react";
|
|
5
5
|
import { FontCss } from "../fontCss";
|
|
@@ -30,17 +30,24 @@ const SSRProvider = ({
|
|
|
30
30
|
layoutStyle = "web",
|
|
31
31
|
reconnect = getEnv().operationMode === "local",
|
|
32
32
|
dictionary,
|
|
33
|
+
allDictionary,
|
|
33
34
|
of,
|
|
34
35
|
}: SSRProviderProps) => {
|
|
35
36
|
setRequestTheme(theme);
|
|
36
|
-
|
|
37
|
+
|
|
38
|
+
const { lang: activeLocale } = usePage();
|
|
39
|
+
|
|
40
|
+
if (allDictionary) for (const [lng, dict] of Object.entries(allDictionary)) Translator.seed(lng, dict);
|
|
41
|
+
|
|
42
|
+
const activeDictionary = allDictionary?.[activeLocale] ?? dictionary;
|
|
43
|
+
|
|
37
44
|
return (
|
|
38
45
|
<Load.Page
|
|
39
46
|
of={of}
|
|
40
47
|
loader={async () => {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
return { lang } as const;
|
|
48
|
+
if (!router.isInitialized)
|
|
49
|
+
router.init({ type: "ssr", side: "server", lang: activeLocale, prefix });
|
|
50
|
+
return { lang: activeLocale } as const;
|
|
44
51
|
}}
|
|
45
52
|
render={({ lang }) => (
|
|
46
53
|
<SSRWrapper
|
|
@@ -53,7 +60,7 @@ const SSRProvider = ({
|
|
|
53
60
|
prefix={prefix}
|
|
54
61
|
layoutStyle={layoutStyle}
|
|
55
62
|
>
|
|
56
|
-
<ClientWrapper theme={theme} lang={lang} reconnect={reconnect} dictionary={
|
|
63
|
+
<ClientWrapper theme={theme} lang={lang} reconnect={reconnect} dictionary={activeDictionary}>
|
|
57
64
|
<Fragment key="children">{Children.toArray(children)}</Fragment>
|
|
58
65
|
<Suspense key="client-inner" fallback={null}>
|
|
59
66
|
<ClientInner />
|