mono-jsx 0.10.0-beta.2 → 0.10.0-beta.20
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/README.md +298 -109
- package/jsx-runtime.mjs +324 -169
- package/package.json +2 -2
- package/types/index.d.ts +3 -0
- package/types/jsx-runtime.d.ts +6 -0
- package/types/jsx.d.ts +42 -3
- package/types/mono.d.ts +64 -45
- package/types/render.d.ts +6 -4
package/README.md
CHANGED
|
@@ -964,8 +964,6 @@ The `<component>` element also supports the `ref` prop, which allows you to cont
|
|
|
964
964
|
- `refresh`: A method to re-render the component with the current name and props.
|
|
965
965
|
|
|
966
966
|
```tsx
|
|
967
|
-
import type { ComponentElement } from "mono-jsx";
|
|
968
|
-
|
|
969
967
|
function App(this: WithRefs<FC, { component: ComponentElement }>) {
|
|
970
968
|
this.effect(() => {
|
|
971
969
|
// updating the component name and props will trigger a re-render of the component
|
|
@@ -1110,16 +1108,208 @@ You can access the `params` object in your route components to get the values of
|
|
|
1110
1108
|
```tsx
|
|
1111
1109
|
// router pattern: "/post/:id"
|
|
1112
1110
|
function Post(this: FC) {
|
|
1113
|
-
this.request.url // "http://localhost:3000/post/123"
|
|
1114
|
-
this.request.params?.id // "123"
|
|
1111
|
+
console.log(this.request.url) // "http://localhost:3000/post/123"
|
|
1112
|
+
console.log(this.request.params?.id) // "123"
|
|
1113
|
+
}
|
|
1114
|
+
```
|
|
1115
|
+
|
|
1116
|
+
You can use `this.app.url` signal to get route URL and parameters:
|
|
1117
|
+
|
|
1118
|
+
```tsx
|
|
1119
|
+
function Post(this: FC) {
|
|
1120
|
+
return (
|
|
1121
|
+
<div>
|
|
1122
|
+
<p>Current URL: {this.$(() => this.app.url.href)}</p>
|
|
1123
|
+
<p>Post id: {this.$(() => this.app.url.params?.id)}</p>
|
|
1124
|
+
</div>
|
|
1125
|
+
)
|
|
1126
|
+
}
|
|
1127
|
+
```
|
|
1128
|
+
|
|
1129
|
+
### Using `this.app.url` Signal
|
|
1130
|
+
|
|
1131
|
+
The `this.app.url` in a component is an app-level signal that contains the current route URL and parameters. It is automatically updated when the route changes, so you can use it to display the current URL in your components or control the view with `<show>`, `<hidden>` or `<switch>` elements:
|
|
1132
|
+
|
|
1133
|
+
```tsx
|
|
1134
|
+
function App(this: FC) {
|
|
1135
|
+
return (
|
|
1136
|
+
<div>
|
|
1137
|
+
<h1>Current Pathname: {this.$(() => this.app.url.pathname)}</h1>
|
|
1138
|
+
</div>
|
|
1139
|
+
)
|
|
1140
|
+
}
|
|
1141
|
+
```
|
|
1142
|
+
|
|
1143
|
+
### Navigation between Pages
|
|
1144
|
+
|
|
1145
|
+
To navigate between pages, you can use `<a>` elements with `href` props that match the defined routes. The router will intercept the click events of these links and fetch the corresponding route component without reloading the page:
|
|
1146
|
+
|
|
1147
|
+
```tsx
|
|
1148
|
+
export default {
|
|
1149
|
+
fetch: (req) => (
|
|
1150
|
+
<html request={req} routes={routes}>
|
|
1151
|
+
<nav>
|
|
1152
|
+
<a href="/">Home</a>
|
|
1153
|
+
<a href="/about">About</a>
|
|
1154
|
+
<a href="/blog">Blog</a>
|
|
1155
|
+
</nav>
|
|
1156
|
+
<router />
|
|
1157
|
+
</html>
|
|
1158
|
+
)
|
|
1159
|
+
}
|
|
1160
|
+
```
|
|
1161
|
+
|
|
1162
|
+
You can also use the `navigate` method of the `<router>` element to navigate to a new route programmatically.
|
|
1163
|
+
|
|
1164
|
+
```tsx
|
|
1165
|
+
function App(this: FC<{}, { router: RouterElement }>) {
|
|
1166
|
+
return (
|
|
1167
|
+
<>
|
|
1168
|
+
<header>
|
|
1169
|
+
<button
|
|
1170
|
+
onClick={() => this.refs.router.navigate("/about", { replace: false, refresh: false })}
|
|
1171
|
+
>About</button>
|
|
1172
|
+
</header>
|
|
1173
|
+
<router ref={this.refs.router} />
|
|
1174
|
+
</>
|
|
1175
|
+
)
|
|
1176
|
+
}
|
|
1177
|
+
```
|
|
1178
|
+
|
|
1179
|
+
### Nav Links
|
|
1180
|
+
|
|
1181
|
+
Links under the `<nav>` element will be treated as navigation links by the router. When the `href` of a nav link matches a route, an active class will be added to the link element. By default, the active class is `active`, but you can customize it by setting the `data-active-class` prop on the `<nav>` element. You can add styles for the active link using nested CSS selectors in the `style` prop of the `<nav>` element.
|
|
1182
|
+
|
|
1183
|
+
```tsx
|
|
1184
|
+
export default {
|
|
1185
|
+
fetch: (req) => (
|
|
1186
|
+
<html request={req} routes={routes}>
|
|
1187
|
+
<nav style={{ "& a.active": { fontWeight: "bold" } }} data-active-class="active">
|
|
1188
|
+
<a href="/">Home</a>
|
|
1189
|
+
<a href="/about">About</a>
|
|
1190
|
+
<a href="/blog">Blog</a>
|
|
1191
|
+
</nav>
|
|
1192
|
+
<router />
|
|
1193
|
+
</html>
|
|
1194
|
+
)
|
|
1115
1195
|
}
|
|
1116
1196
|
```
|
|
1117
1197
|
|
|
1118
|
-
###
|
|
1198
|
+
### Adding Page Metadata
|
|
1119
1199
|
|
|
1120
|
-
|
|
1200
|
+
You can add metadata to the route component by setting the `metadata` property on the route component.
|
|
1121
1201
|
|
|
1122
|
-
|
|
1202
|
+
```tsx
|
|
1203
|
+
function Home(this: FC) {
|
|
1204
|
+
return <p>Home</p>
|
|
1205
|
+
}
|
|
1206
|
+
|
|
1207
|
+
Home.metadata = {
|
|
1208
|
+
title: "Home",
|
|
1209
|
+
description: "Home page",
|
|
1210
|
+
}
|
|
1211
|
+
|
|
1212
|
+
const routes = {
|
|
1213
|
+
"/": Home,
|
|
1214
|
+
}
|
|
1215
|
+
```
|
|
1216
|
+
|
|
1217
|
+
Or use `getMetadata` property on the route component to dynamically generate the metadata.
|
|
1218
|
+
|
|
1219
|
+
```tsx
|
|
1220
|
+
async function Post(this: FC) {
|
|
1221
|
+
const post = await getPost(this.request.params.slug)
|
|
1222
|
+
return <div>
|
|
1223
|
+
<h1>{post.title}</h1>
|
|
1224
|
+
<h2>{post.description}</h2>
|
|
1225
|
+
<div>{post.content}</div>
|
|
1226
|
+
</div>
|
|
1227
|
+
}
|
|
1228
|
+
|
|
1229
|
+
Post.getMetadata = async function(this: FC) {
|
|
1230
|
+
const post = await getPost(this.request.params.slug)
|
|
1231
|
+
return {
|
|
1232
|
+
title: post.title,
|
|
1233
|
+
description: post.description,
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1236
|
+
|
|
1237
|
+
const routes = {
|
|
1238
|
+
"/post/:slug": Post,
|
|
1239
|
+
}
|
|
1240
|
+
```
|
|
1241
|
+
|
|
1242
|
+
You can define global metadata with the `metadata` prop on the root `<html>` element. It applies to every page in your app, and page-specific metadata takes precedence over these global values.
|
|
1243
|
+
|
|
1244
|
+
To render metadata, add `<metadata />` inside the `head` tag:
|
|
1245
|
+
|
|
1246
|
+
```tsx
|
|
1247
|
+
export default {
|
|
1248
|
+
fetch: (req) => (
|
|
1249
|
+
<html
|
|
1250
|
+
request={req}
|
|
1251
|
+
routes={routes}
|
|
1252
|
+
metadata={{ title: "My App" }}
|
|
1253
|
+
>
|
|
1254
|
+
<head>
|
|
1255
|
+
<metadata /> { /* <- `<title>My App<title>` will be rendered here */}
|
|
1256
|
+
</head>
|
|
1257
|
+
<body>
|
|
1258
|
+
<rouer>
|
|
1259
|
+
<p>Page not found</p>
|
|
1260
|
+
</rouer>
|
|
1261
|
+
</body>
|
|
1262
|
+
</html>
|
|
1263
|
+
)
|
|
1264
|
+
}
|
|
1265
|
+
```
|
|
1266
|
+
|
|
1267
|
+
### Fallback (404)
|
|
1268
|
+
|
|
1269
|
+
You can add fallback(404) content to the `<router>` element as children, which will be displayed when no route matches the current URL.
|
|
1270
|
+
|
|
1271
|
+
```tsx
|
|
1272
|
+
export default {
|
|
1273
|
+
fetch: (req) => (
|
|
1274
|
+
<html request={req} routes={routes}>
|
|
1275
|
+
<router>
|
|
1276
|
+
<p>Page Not Found</p>
|
|
1277
|
+
<p>Back to <a href="/">Home</a></p>
|
|
1278
|
+
</router>
|
|
1279
|
+
</html>
|
|
1280
|
+
)
|
|
1281
|
+
}
|
|
1282
|
+
```
|
|
1283
|
+
|
|
1284
|
+
### Route Client Caching
|
|
1285
|
+
|
|
1286
|
+
By default, the router client caches the html content from the server. To disable the caching, you can add the `dynamic` option to the route component.
|
|
1287
|
+
|
|
1288
|
+
```tsx
|
|
1289
|
+
// Home is a static route that can be cached on the client side
|
|
1290
|
+
function Home(this: FC) {
|
|
1291
|
+
return <p>Home</p>
|
|
1292
|
+
}
|
|
1293
|
+
|
|
1294
|
+
// Dash is a dynamic route that will not be cached on the client side
|
|
1295
|
+
// it will be fetched from the server on every navigation
|
|
1296
|
+
function Dash(this: FC) {
|
|
1297
|
+
const user = this.session.get<{ name: string }>("user")
|
|
1298
|
+
return <p>Welcome back, {user?.name}!</p>
|
|
1299
|
+
}
|
|
1300
|
+
Dash.dynamic = true;
|
|
1301
|
+
|
|
1302
|
+
const routes = {
|
|
1303
|
+
"/": Home,
|
|
1304
|
+
"/dash": Dash,
|
|
1305
|
+
}
|
|
1306
|
+
```
|
|
1307
|
+
|
|
1308
|
+
## Using Route Form
|
|
1309
|
+
|
|
1310
|
+
mono-jsx allows you to define a `FormHandler` function for route components to handle form data from client side form submissions. To submit the form data to the `FormHandler` function, you need to add the `route` prop to the `<form>` element.
|
|
1311
|
+
|
|
1312
|
+
The `FormHandler` function is also a component, so you can use all the features of the component system in it. mono-jsx provides two built-in elements to allow you to control the post-submit behavior:
|
|
1123
1313
|
|
|
1124
1314
|
- `<invalid for="...">{message}</invalid>` to set custom validation state for the form elements.
|
|
1125
1315
|
- `<redirect to="..." />` to redirect to a new route/URL.
|
|
@@ -1147,19 +1337,19 @@ Login.FormHandler = function(this: FC, data: FormData) {
|
|
|
1147
1337
|
|
|
1148
1338
|
const routes = {
|
|
1149
1339
|
"/login": Login,
|
|
1150
|
-
// ... other routes ...
|
|
1151
1340
|
}
|
|
1152
1341
|
```
|
|
1153
1342
|
|
|
1154
|
-
> [!
|
|
1343
|
+
> [!TIP]
|
|
1155
1344
|
> You can use `:invalid` CSS selector to style the form elements with invalid state.
|
|
1156
1345
|
|
|
1157
|
-
|
|
1158
|
-
|
|
1346
|
+
### Using `<formslot>` element
|
|
1347
|
+
|
|
1348
|
+
You can return regular HTML elements from the form handler function. By default, the returned HTML is appended to the form element. Use the `<formslot>` element to control where the returned content is inserted. If any `<formslot>` element exists, the `mode` attribute on the `<form route>` element is ignored. `<formslot>` supports the following modes:
|
|
1159
1349
|
|
|
1160
|
-
-
|
|
1161
|
-
-
|
|
1162
|
-
-
|
|
1350
|
+
- **"replaceChildren"** (default): Replace children of the `<formslot>` element with the returned HTML.
|
|
1351
|
+
- **"insertafter"**: Insert HTML after the `<formslot>` element.
|
|
1352
|
+
- **"insertbefore"**: Insert HTML before the `<formslot>` element.
|
|
1163
1353
|
|
|
1164
1354
|
```tsx
|
|
1165
1355
|
function MyRoute(this: FC) {
|
|
@@ -1182,115 +1372,103 @@ MyRoute.FormHandler = function(this: FC, data: FormData) {
|
|
|
1182
1372
|
}
|
|
1183
1373
|
```
|
|
1184
1374
|
|
|
1185
|
-
You can
|
|
1375
|
+
You can add the `name` prop to specify the name of the formslot element. And use `formslot` prop in the form handler function to specify the name of the slot to insert the HTML into.
|
|
1186
1376
|
|
|
1187
1377
|
```tsx
|
|
1188
1378
|
function MyRoute(this: FC) {
|
|
1189
1379
|
return (
|
|
1190
1380
|
<div>
|
|
1191
|
-
<formslot name="message" />
|
|
1192
1381
|
<form route>
|
|
1193
1382
|
<button type="submit">Send</button>
|
|
1383
|
+
<formslot name="info" /> { /* <- "This is info message" will be inserted here */ }
|
|
1384
|
+
<formslot name="error" /> { /* <- "This is error message" will be inserted here */ }
|
|
1194
1385
|
</form>
|
|
1195
1386
|
</div>
|
|
1196
1387
|
)
|
|
1197
1388
|
}
|
|
1198
1389
|
|
|
1199
1390
|
MyRoute.FormHandler = function(this: FC, data: FormData) {
|
|
1200
|
-
return
|
|
1391
|
+
return (
|
|
1392
|
+
<>
|
|
1393
|
+
<p formslot="info">This is info message</p>
|
|
1394
|
+
<p formslot="error">This is error message</p>
|
|
1395
|
+
</>
|
|
1396
|
+
)
|
|
1201
1397
|
}
|
|
1202
1398
|
```
|
|
1203
1399
|
|
|
1204
|
-
`formslot`
|
|
1400
|
+
The `formslot` prop also accepts the following special values:
|
|
1401
|
+
|
|
1402
|
+
- `:form`: Replace the form element with the returned HTML.
|
|
1403
|
+
- `:router`: Replace the children of current `<router>` element with the returned HTML.
|
|
1404
|
+
- `:root`: Replace the children of the page with the returned HTML.
|
|
1205
1405
|
|
|
1206
1406
|
```tsx
|
|
1207
1407
|
function MyRoute(this: FC) {
|
|
1208
1408
|
return (
|
|
1209
|
-
|
|
1210
|
-
|
|
1409
|
+
{ /* the form will be replaced with the returned HTML after the form is submitted */ }
|
|
1410
|
+
<form route>
|
|
1211
1411
|
<button type="submit">Send</button>
|
|
1212
|
-
<formslot onUpdate={(evt) => console.log("message updated:", evt.target.textContent)} />
|
|
1213
1412
|
</form>
|
|
1214
1413
|
)
|
|
1215
1414
|
}
|
|
1216
1415
|
|
|
1217
1416
|
MyRoute.FormHandler = function(this: FC, data: FormData) {
|
|
1218
|
-
return <p
|
|
1417
|
+
return <p formslot=":form">Form submitted</p>
|
|
1219
1418
|
}
|
|
1220
1419
|
```
|
|
1221
1420
|
|
|
1222
|
-
The `
|
|
1223
|
-
|
|
1224
|
-
```tsx
|
|
1225
|
-
<formslot onUpdate={(evt) => console.log("message updated:", evt.target.textContent)} hidden />
|
|
1226
|
-
```
|
|
1227
|
-
|
|
1228
|
-
### Using `this.app.url` Signal
|
|
1229
|
-
|
|
1230
|
-
`this.app.url` is an app-level signal that contains the current route URL and parameters. The `this.app.url` signal is automatically updated when the route changes, so you can use it to display the current URL in your components or control the view with `<show>`, `<hidden>` or `<switch>` elements:
|
|
1421
|
+
The `<formslot>` element accepts a `onUpdate` prop as a callback function that will be invoked when the formslot element is updated.
|
|
1231
1422
|
|
|
1232
1423
|
```tsx
|
|
1233
|
-
function
|
|
1424
|
+
function MyRoute(this: FC) {
|
|
1234
1425
|
return (
|
|
1235
|
-
<
|
|
1236
|
-
<
|
|
1237
|
-
|
|
1426
|
+
<form>
|
|
1427
|
+
<input type="text" name="message" placeholder="Type Message..." />
|
|
1428
|
+
<button type="submit">Send</button>
|
|
1429
|
+
<formslot hidden onUpdate={(evt) => console.log("message updated:", evt.target.textContent)} />
|
|
1430
|
+
</form>
|
|
1238
1431
|
)
|
|
1239
1432
|
}
|
|
1433
|
+
|
|
1434
|
+
MyRoute.FormHandler = function(this: FC, data: FormData) {
|
|
1435
|
+
return <p>{data.get("message")}</p>
|
|
1436
|
+
}
|
|
1240
1437
|
```
|
|
1241
1438
|
|
|
1242
|
-
|
|
1439
|
+
> [!TIP]
|
|
1440
|
+
> You can use the `hidden` prop with the `onUpdate` prop to hide the formslot element. It is useful when you only want to know what content is returned from the form handler and don't want to display it on the page.
|
|
1243
1441
|
|
|
1244
|
-
|
|
1442
|
+
### Submitting State
|
|
1245
1443
|
|
|
1246
|
-
|
|
1247
|
-
export default {
|
|
1248
|
-
fetch: (req) => (
|
|
1249
|
-
<html request={req} routes={routes}>
|
|
1250
|
-
<nav>
|
|
1251
|
-
<a href="/">Home</a>
|
|
1252
|
-
<a href="/about">About</a>
|
|
1253
|
-
<a href="/blog">Blog</a>
|
|
1254
|
-
</nav>
|
|
1255
|
-
<router />
|
|
1256
|
-
</html>
|
|
1257
|
-
)
|
|
1258
|
-
}
|
|
1259
|
-
```
|
|
1444
|
+
When a `<form route>` is submitted, mono-jsx automatically manages a short "submitting" state on the client:
|
|
1260
1445
|
|
|
1261
|
-
|
|
1446
|
+
- Adds a CSS class to the form while the request is in flight (default: `submitting`).
|
|
1447
|
+
- Disables all form controls to prevent double submit, then restores their original disabled state.
|
|
1448
|
+
- Clears the current content of local `<formslot>` elements before inserting the next response payload.
|
|
1262
1449
|
|
|
1263
|
-
|
|
1450
|
+
You can use the `data-submitting-class` attribute to customize the submitting state class name:
|
|
1264
1451
|
|
|
1265
1452
|
```tsx
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
<
|
|
1269
|
-
<
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
</nav>
|
|
1274
|
-
<router />
|
|
1275
|
-
</html>
|
|
1453
|
+
function Contact(this: FC) {
|
|
1454
|
+
return (
|
|
1455
|
+
<form route data-submitting-class="is-loading">
|
|
1456
|
+
<input type="email" name="email" required />
|
|
1457
|
+
<button type="submit">Subscribe</button>
|
|
1458
|
+
<formslot />
|
|
1459
|
+
</form>
|
|
1276
1460
|
)
|
|
1277
1461
|
}
|
|
1278
|
-
```
|
|
1279
1462
|
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1463
|
+
Contact.FormHandler = function(this: FC, data: FormData) {
|
|
1464
|
+
return <p>Thanks, {data.get("email")}!</p>
|
|
1465
|
+
}
|
|
1466
|
+
```
|
|
1283
1467
|
|
|
1284
|
-
```
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
<router>
|
|
1289
|
-
<p>Page Not Found</p>
|
|
1290
|
-
<p>Back to <a href="/">Home</a></p>
|
|
1291
|
-
</router>
|
|
1292
|
-
</html>
|
|
1293
|
-
)
|
|
1468
|
+
```css
|
|
1469
|
+
form.is-loading {
|
|
1470
|
+
opacity: 0.6;
|
|
1471
|
+
pointer-events: none;
|
|
1294
1472
|
}
|
|
1295
1473
|
```
|
|
1296
1474
|
|
|
@@ -1343,17 +1521,39 @@ export default {
|
|
|
1343
1521
|
### Session Storage API
|
|
1344
1522
|
|
|
1345
1523
|
```ts
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1524
|
+
export interface Session {
|
|
1525
|
+
/**
|
|
1526
|
+
* The session ID.
|
|
1527
|
+
*/
|
|
1528
|
+
readonly sessionId: string;
|
|
1529
|
+
/**
|
|
1530
|
+
* If true, update the session cookie to the client.
|
|
1531
|
+
*/
|
|
1532
|
+
readonly isDirty: boolean;
|
|
1533
|
+
/**
|
|
1534
|
+
* If true, the session is expired.
|
|
1535
|
+
*/
|
|
1536
|
+
readonly isExpired: boolean;
|
|
1537
|
+
/**
|
|
1538
|
+
* Gets a value from the session.
|
|
1539
|
+
*/
|
|
1540
|
+
get<T = unknown>(key: string): T | undefined;
|
|
1541
|
+
/**
|
|
1542
|
+
* Gets all the entries from the session.
|
|
1543
|
+
*/
|
|
1544
|
+
all(): Record<string, unknown>;
|
|
1545
|
+
/**
|
|
1546
|
+
* Sets a value in the session.
|
|
1547
|
+
*/
|
|
1548
|
+
set(key: string, value: string | number | boolean | any[] | Record<string, unknown>): void;
|
|
1549
|
+
/**
|
|
1550
|
+
* Deletes a value from the session.
|
|
1551
|
+
*/
|
|
1552
|
+
delete(key: string): void;
|
|
1553
|
+
/**
|
|
1554
|
+
* Destroys the session.
|
|
1555
|
+
*/
|
|
1556
|
+
destroy(): void;
|
|
1357
1557
|
}
|
|
1358
1558
|
```
|
|
1359
1559
|
|
|
@@ -1368,14 +1568,14 @@ mono-jsx provides a built-in RPC API that allows you to call functions on the se
|
|
|
1368
1568
|
import { createRPC } from "mono-jsx"
|
|
1369
1569
|
|
|
1370
1570
|
const rpc = createRPC({
|
|
1371
|
-
whoami: () => ({ name: "John" })
|
|
1571
|
+
whoami: () => ({ name: "John Wick" })
|
|
1372
1572
|
})
|
|
1373
1573
|
|
|
1374
|
-
function App(this: FC<{ user?: { name: string } }>) {
|
|
1574
|
+
function App(this: FC<{ user?: { id: number, name: string } }>) {
|
|
1375
1575
|
return (
|
|
1376
1576
|
<div>
|
|
1377
1577
|
<show when={this.user}>
|
|
1378
|
-
<p>Welcome, {this.user!.name}
|
|
1578
|
+
<p>Welcome, {this.user!.name}</p>
|
|
1379
1579
|
</show>
|
|
1380
1580
|
<button onClick={async () => this.user = await rpc.whoami()}>Who am I?</button>
|
|
1381
1581
|
</div>
|
|
@@ -1402,38 +1602,27 @@ You can access the request info in RPC functions by using the `this` scope:
|
|
|
1402
1602
|
- `this.context`: The [context](#using-context) object.
|
|
1403
1603
|
- `this.session`: The [session](#session-storage-api) storage.
|
|
1404
1604
|
|
|
1405
|
-
```
|
|
1406
|
-
type Admin = {
|
|
1407
|
-
isAdmin: (id: number) => boolean;
|
|
1408
|
-
}
|
|
1409
|
-
|
|
1605
|
+
```tsx
|
|
1410
1606
|
const rpc = createRPC({
|
|
1411
|
-
whoami: function (this: WithContext<RPC, {
|
|
1412
|
-
const {
|
|
1413
|
-
const user = this.session.get<{ name: string }>("user")
|
|
1607
|
+
whoami: function (this: WithContext<RPC, { group: string }>) {
|
|
1608
|
+
const user = this.session.get<{ id: number, name: string }>("user")
|
|
1414
1609
|
return {
|
|
1610
|
+
...user,
|
|
1611
|
+
group: this.context.group,
|
|
1415
1612
|
ip: this.request.headers.get("x-real-ip"),
|
|
1416
|
-
isAdmin: user ? admin.isAdmin(user.id) : false,
|
|
1417
|
-
user: user,
|
|
1418
1613
|
}
|
|
1419
1614
|
},
|
|
1420
1615
|
})
|
|
1421
1616
|
|
|
1422
|
-
function App(this: FC) {
|
|
1423
|
-
return (
|
|
1424
|
-
<button onClick={async () => console.log(await rpc.whoami())}>Who am I?</button>
|
|
1425
|
-
)
|
|
1426
|
-
}
|
|
1427
|
-
|
|
1428
1617
|
export default {
|
|
1429
1618
|
fetch: (req) => (
|
|
1430
1619
|
<html
|
|
1431
1620
|
request={req}
|
|
1432
1621
|
expose={{ rpc }}
|
|
1433
1622
|
session={{ cookie: { secret: "..." } }}
|
|
1434
|
-
context={{
|
|
1623
|
+
context={{ group: "admin" }}
|
|
1435
1624
|
>
|
|
1436
|
-
<
|
|
1625
|
+
<button onClick={async () => console.log(await rpc.whoami())}>Who am I?</button>
|
|
1437
1626
|
</html>
|
|
1438
1627
|
)
|
|
1439
1628
|
}
|