@alepha/react 0.10.7 → 0.11.0
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/index.browser.js +6 -6
- package/dist/index.browser.js.map +1 -1
- package/dist/index.d.ts +76 -56
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +27 -9
- package/dist/index.js.map +1 -1
- package/package.json +11 -11
- package/src/hooks/useQueryParams.ts +5 -2
- package/src/hooks/useSchema.ts +2 -2
- package/src/providers/ReactPageProvider.ts +3 -3
- package/src/providers/ReactServerProvider.ts +37 -5
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alepha/react",
|
|
3
3
|
"description": "Build server-side rendered (SSR) or single-page React applications.",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.11.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
7
7
|
"node": ">=22.0.0"
|
|
@@ -17,22 +17,22 @@
|
|
|
17
17
|
"src"
|
|
18
18
|
],
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"@alepha/core": "0.
|
|
21
|
-
"@alepha/datetime": "0.
|
|
22
|
-
"@alepha/logger": "0.
|
|
23
|
-
"@alepha/router": "0.
|
|
24
|
-
"@alepha/server": "0.
|
|
25
|
-
"@alepha/server-cache": "0.
|
|
26
|
-
"@alepha/server-links": "0.
|
|
27
|
-
"@alepha/server-static": "0.
|
|
20
|
+
"@alepha/core": "0.11.0",
|
|
21
|
+
"@alepha/datetime": "0.11.0",
|
|
22
|
+
"@alepha/logger": "0.11.0",
|
|
23
|
+
"@alepha/router": "0.11.0",
|
|
24
|
+
"@alepha/server": "0.11.0",
|
|
25
|
+
"@alepha/server-cache": "0.11.0",
|
|
26
|
+
"@alepha/server-links": "0.11.0",
|
|
27
|
+
"@alepha/server-static": "0.11.0"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
|
-
"@biomejs/biome": "^2.2
|
|
30
|
+
"@biomejs/biome": "^2.3.2",
|
|
31
31
|
"@types/react": "^19.2.2",
|
|
32
32
|
"@types/react-dom": "^19.2.2",
|
|
33
33
|
"react": "^19.2.0",
|
|
34
34
|
"react-dom": "^19.2.0",
|
|
35
|
-
"tsdown": "^0.15.
|
|
35
|
+
"tsdown": "^0.15.11",
|
|
36
36
|
"typescript": "^5.9.3",
|
|
37
37
|
"vitest": "^3.2.4"
|
|
38
38
|
},
|
|
@@ -44,7 +44,7 @@ export interface UseQueryParamsHookOptions {
|
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
const encode = (alepha: Alepha, schema: TObject, data: any) => {
|
|
47
|
-
return btoa(JSON.stringify(alepha.
|
|
47
|
+
return btoa(JSON.stringify(alepha.codec.decode(schema, data)));
|
|
48
48
|
};
|
|
49
49
|
|
|
50
50
|
const decode = <T extends TObject>(
|
|
@@ -53,7 +53,10 @@ const decode = <T extends TObject>(
|
|
|
53
53
|
data: any,
|
|
54
54
|
): Static<T> | undefined => {
|
|
55
55
|
try {
|
|
56
|
-
return alepha.
|
|
56
|
+
return alepha.codec.decode(
|
|
57
|
+
schema,
|
|
58
|
+
JSON.parse(atob(decodeURIComponent(data))),
|
|
59
|
+
);
|
|
57
60
|
} catch {
|
|
58
61
|
return;
|
|
59
62
|
}
|
package/src/hooks/useSchema.ts
CHANGED
|
@@ -25,11 +25,11 @@ export const useSchema = <TConfig extends RequestConfigSchema>(
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
const opts: FetchOptions = {
|
|
28
|
-
|
|
28
|
+
localCache: true,
|
|
29
29
|
};
|
|
30
30
|
|
|
31
31
|
httpClient
|
|
32
|
-
.fetch(`${LinkProvider.path.apiLinks}/${name}/schema`,
|
|
32
|
+
.fetch(`${LinkProvider.path.apiLinks}/${name}/schema`, opts)
|
|
33
33
|
.then((it) => setSchema(it.data as UseSchemaReturn<TConfig>));
|
|
34
34
|
}, [name]);
|
|
35
35
|
|
|
@@ -119,7 +119,7 @@ export class ReactPageProvider {
|
|
|
119
119
|
typeof value[key] === "string"
|
|
120
120
|
) {
|
|
121
121
|
try {
|
|
122
|
-
value[key] = this.alepha.
|
|
122
|
+
value[key] = this.alepha.codec.decode(
|
|
123
123
|
schema.properties[key],
|
|
124
124
|
decodeURIComponent(value[key]),
|
|
125
125
|
);
|
|
@@ -161,7 +161,7 @@ export class ReactPageProvider {
|
|
|
161
161
|
try {
|
|
162
162
|
this.convertStringObjectToObject(route.schema?.query, state.query);
|
|
163
163
|
config.query = route.schema?.query
|
|
164
|
-
? this.alepha.
|
|
164
|
+
? this.alepha.codec.decode(route.schema.query, state.query)
|
|
165
165
|
: {};
|
|
166
166
|
} catch (e) {
|
|
167
167
|
it.error = e as Error;
|
|
@@ -170,7 +170,7 @@ export class ReactPageProvider {
|
|
|
170
170
|
|
|
171
171
|
try {
|
|
172
172
|
config.params = route.schema?.params
|
|
173
|
-
? this.alepha.
|
|
173
|
+
? this.alepha.codec.decode(route.schema.params, state.params)
|
|
174
174
|
: {};
|
|
175
175
|
} catch (e) {
|
|
176
176
|
it.error = e as Error;
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
$inject,
|
|
7
7
|
Alepha,
|
|
8
8
|
AlephaError,
|
|
9
|
+
type Configurable,
|
|
9
10
|
type Static,
|
|
10
11
|
t,
|
|
11
12
|
} from "@alepha/core";
|
|
@@ -17,7 +18,10 @@ import {
|
|
|
17
18
|
ServerTimingProvider,
|
|
18
19
|
} from "@alepha/server";
|
|
19
20
|
import { ServerLinksProvider } from "@alepha/server-links";
|
|
20
|
-
import {
|
|
21
|
+
import {
|
|
22
|
+
type ServeDescriptorOptions,
|
|
23
|
+
ServerStaticProvider,
|
|
24
|
+
} from "@alepha/server-static";
|
|
21
25
|
import { renderToString } from "react-dom/server";
|
|
22
26
|
import {
|
|
23
27
|
$page,
|
|
@@ -36,7 +40,7 @@ const envSchema = t.object({
|
|
|
36
40
|
REACT_SERVER_DIST: t.text({ default: "public" }),
|
|
37
41
|
REACT_SERVER_PREFIX: t.text({ default: "" }),
|
|
38
42
|
REACT_SSR_ENABLED: t.optional(t.boolean()),
|
|
39
|
-
REACT_ROOT_ID: t.text({ default: "root" }),
|
|
43
|
+
REACT_ROOT_ID: t.text({ default: "root" }), // TODO: move to ReactPageProvider.options?
|
|
40
44
|
REACT_SERVER_TEMPLATE: t.optional(
|
|
41
45
|
t.text({
|
|
42
46
|
size: "rich",
|
|
@@ -51,21 +55,35 @@ declare module "@alepha/core" {
|
|
|
51
55
|
}
|
|
52
56
|
}
|
|
53
57
|
|
|
54
|
-
export
|
|
58
|
+
export interface ReactServerProviderOptions {
|
|
59
|
+
/**
|
|
60
|
+
* Override default options for the static file server.
|
|
61
|
+
* > Static file server is only created in non-serverless production mode.
|
|
62
|
+
*/
|
|
63
|
+
static?: Partial<Omit<ServeDescriptorOptions, "root">>;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export class ReactServerProvider implements Configurable {
|
|
55
67
|
protected readonly log = $logger();
|
|
56
68
|
protected readonly alepha = $inject(Alepha);
|
|
69
|
+
protected readonly env = $env(envSchema);
|
|
57
70
|
protected readonly pageApi = $inject(ReactPageProvider);
|
|
58
71
|
protected readonly serverProvider = $inject(ServerProvider);
|
|
59
72
|
protected readonly serverStaticProvider = $inject(ServerStaticProvider);
|
|
60
73
|
protected readonly serverRouterProvider = $inject(ServerRouterProvider);
|
|
61
74
|
protected readonly serverTimingProvider = $inject(ServerTimingProvider);
|
|
62
|
-
|
|
75
|
+
|
|
63
76
|
protected readonly ROOT_DIV_REGEX = new RegExp(
|
|
64
77
|
`<div([^>]*)\\s+id=["']${this.env.REACT_ROOT_ID}["']([^>]*)>(.*?)<\\/div>`,
|
|
65
78
|
"is",
|
|
66
79
|
);
|
|
67
80
|
protected preprocessedTemplate: PreprocessedTemplate | null = null;
|
|
68
81
|
|
|
82
|
+
public options: ReactServerProviderOptions = {};
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Configure the React server provider.
|
|
86
|
+
*/
|
|
69
87
|
public readonly onConfigure = $hook({
|
|
70
88
|
on: "configure",
|
|
71
89
|
handler: async () => {
|
|
@@ -174,6 +192,9 @@ export class ReactServerProvider {
|
|
|
174
192
|
}
|
|
175
193
|
}
|
|
176
194
|
|
|
195
|
+
/**
|
|
196
|
+
* Get the public directory path where static files are located.
|
|
197
|
+
*/
|
|
177
198
|
protected getPublicDirectory(): string {
|
|
178
199
|
const maybe = [
|
|
179
200
|
join(process.cwd(), `dist/${this.env.REACT_SERVER_DIST}`),
|
|
@@ -189,13 +210,24 @@ export class ReactServerProvider {
|
|
|
189
210
|
return "";
|
|
190
211
|
}
|
|
191
212
|
|
|
213
|
+
/**
|
|
214
|
+
* Configure the static file server to serve files from the given root directory.
|
|
215
|
+
*/
|
|
192
216
|
protected async configureStaticServer(root: string) {
|
|
193
217
|
await this.serverStaticProvider.createStaticServer({
|
|
194
218
|
root,
|
|
195
219
|
path: this.env.REACT_SERVER_PREFIX,
|
|
220
|
+
cacheControl: {
|
|
221
|
+
maxAge: 3600,
|
|
222
|
+
immutable: true,
|
|
223
|
+
},
|
|
224
|
+
...this.options.static,
|
|
196
225
|
});
|
|
197
226
|
}
|
|
198
227
|
|
|
228
|
+
/**
|
|
229
|
+
* Configure Vite for SSR.
|
|
230
|
+
*/
|
|
199
231
|
protected async configureVite(ssrEnabled: boolean) {
|
|
200
232
|
if (!ssrEnabled) {
|
|
201
233
|
// do nothing, vite will handle everything for us
|
|
@@ -286,7 +318,7 @@ export class ReactServerProvider {
|
|
|
286
318
|
const { url, reply, query, params } = serverRequest;
|
|
287
319
|
const template = await templateLoader();
|
|
288
320
|
if (!template) {
|
|
289
|
-
throw new
|
|
321
|
+
throw new AlephaError("Template not found");
|
|
290
322
|
}
|
|
291
323
|
|
|
292
324
|
this.log.trace("Rendering page", {
|