react-fate 1.0.0-rc.0 → 1.0.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/README.md +98 -29
- package/docs/api/functions/FateClient.md +1 -1
- package/docs/api/functions/useFateClient.md +1 -1
- package/docs/api/functions/useListView.md +1 -1
- package/docs/api/functions/useLiveListView.md +1 -1
- package/docs/api/functions/useLiveView.md +1 -1
- package/docs/api/functions/useRequest.md +1 -1
- package/docs/api/functions/useView.md +1 -1
- package/docs/api/type-aliases/InferFateAPI.md +1 -1
- package/docs/guide/actions.md +46 -4
- package/docs/guide/live-views.md +15 -2
- package/docs/guide/requests.md +1 -1
- package/docs/guide/server-integration.md +35 -21
- package/docs/guide/views.md +1 -1
- package/docs/guide/void-integration.md +3 -3
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -245,7 +245,7 @@ This code fetches the author associated with the Post and makes it available to
|
|
|
245
245
|
In fate, views are composable and reusable. Instead of inlining the selection, we can define a `UserView` and compose it into the `PostView` like this:
|
|
246
246
|
|
|
247
247
|
```tsx
|
|
248
|
-
import type { Post, User } from '@your-org/server/
|
|
248
|
+
import type { Post, User } from '@your-org/server/views';
|
|
249
249
|
import { view } from 'react-fate';
|
|
250
250
|
|
|
251
251
|
export const UserView = view<User>()({
|
|
@@ -446,7 +446,7 @@ This component suspends or throws errors, which bubble up to the nearest error b
|
|
|
446
446
|
|
|
447
447
|
> [!NOTE]
|
|
448
448
|
>
|
|
449
|
-
> `useRequest`
|
|
449
|
+
> `useRequest` may issue multiple operations in the same render pass. fate transports can batch those operations into fewer network requests: the native HTTP transport batches same-microtask operations into one `POST /fate` request, and the tRPC adapter can use tRPC's [HTTP Batch Link](https://trpc.io/docs/client/links/httpBatchLink).
|
|
450
450
|
|
|
451
451
|
### Requesting Objects by ID
|
|
452
452
|
|
|
@@ -660,7 +660,7 @@ The API mirrors `useView`: pass a view and a ref, and get back the same masked d
|
|
|
660
660
|
|
|
661
661
|
### How Live Updates Work
|
|
662
662
|
|
|
663
|
-
The native HTTP transport opens one Server-Sent Events (SSE) connection per
|
|
663
|
+
The native HTTP transport opens one Server-Sent Events (SSE) connection per fate client. When components mount or unmount live views, the client sends subscribe and unsubscribe control messages to the server. The server keeps those selections on the connection and sends updates only for records that connection subscribed to.
|
|
664
664
|
|
|
665
665
|
When the server sends an update, fate normalizes the selected record into the same cache used by requests, actions, and mutations. Components that read affected fields re-render automatically.
|
|
666
666
|
|
|
@@ -670,7 +670,7 @@ Live deletion events remove the record from the normalized cache in the same way
|
|
|
670
670
|
|
|
671
671
|
### Client Setup
|
|
672
672
|
|
|
673
|
-
|
|
673
|
+
Configure the native transport and point the client at your fate endpoint:
|
|
674
674
|
|
|
675
675
|
```tsx
|
|
676
676
|
import { FateClient } from 'react-fate';
|
|
@@ -721,6 +721,19 @@ export const fate = createFateServer<AppContext>({
|
|
|
721
721
|
app.all('/fate/*', createHonoFateHandler(fate));
|
|
722
722
|
```
|
|
723
723
|
|
|
724
|
+
fate keeps a bounded in-memory queue for each native SSE connection while live events are waiting to be resolved and sent. The default limit is `1000` queued events per connection. If a client falls behind and exceeds the limit, fate closes that live connection so server memory cannot grow without bound. You can tune the limit by passing the object form:
|
|
725
|
+
|
|
726
|
+
```tsx
|
|
727
|
+
export const fate = createFateServer<AppContext>({
|
|
728
|
+
live: {
|
|
729
|
+
bus: live,
|
|
730
|
+
maxQueueSize: 500,
|
|
731
|
+
},
|
|
732
|
+
roots: Root,
|
|
733
|
+
sources,
|
|
734
|
+
});
|
|
735
|
+
```
|
|
736
|
+
|
|
724
737
|
Once this is in place, components can switch from `useView` to `useLiveView` without changing their view definitions or return types.
|
|
725
738
|
|
|
726
739
|
### Live List Views
|
|
@@ -889,9 +902,13 @@ fate does not provide hooks for mutations like traditional data fetching librari
|
|
|
889
902
|
- `fate.actions` for use with [`useActionState`](https://react.dev/reference/react/useActionState) and React Actions.
|
|
890
903
|
- `fate.mutations` for traditional imperative mutation calls.
|
|
891
904
|
|
|
892
|
-
|
|
905
|
+
Server mutations are exposed automatically as actions and mutations by fate's Vite plugin. The transport determines where those mutations are declared:
|
|
906
|
+
|
|
907
|
+
- With the [native HTTP transport](/docs/guide/server-integration.md#native-fate-protocol), mutations come from the `mutations` object passed to `createFateServer`.
|
|
908
|
+
- With the [tRPC adapter](/docs/guide/server-integration.md#trpc-fate-setup), mutations come from tRPC mutation procedures exposed through your fate-enabled router.
|
|
909
|
+
- With [Void](/docs/guide/void-integration.md), mutations use the same native fate server shape and are exposed through the Void route helpers.
|
|
893
910
|
|
|
894
|
-
|
|
911
|
+
If you have a mutation named `post.like`, a `LikeButton` component using fate Actions and an async component library could look like this:
|
|
895
912
|
|
|
896
913
|
```tsx
|
|
897
914
|
import { useActionState } from 'react';
|
|
@@ -1026,7 +1043,45 @@ You can call mutations from anywhere, and without waiting for previous mutations
|
|
|
1026
1043
|
|
|
1027
1044
|
### Mutation Server Implementation
|
|
1028
1045
|
|
|
1029
|
-
fate Actions & Mutations are backed by regular
|
|
1046
|
+
fate Actions & Mutations are backed by regular server mutations. If you already know how your fate server is wired, the client-side API above is the same regardless of transport. If not, start with the server setup for your environment:
|
|
1047
|
+
|
|
1048
|
+
- [Native HTTP custom mutations](/docs/guide/server-integration.md#custom-mutations) use `createFateServer({ mutations })`.
|
|
1049
|
+
- [tRPC fate setup](/docs/guide/server-integration.md#trpc-fate-setup) wires fate into your tRPC router; custom writes can use the same `fate.createPlan` and `fate.resolveById` helpers shown there.
|
|
1050
|
+
- [Void integration](/docs/guide/void-integration.md) exposes a native fate server from Void routes; define mutations with the native `createFateServer({ mutations })` API and serve them through `defineVoidFateRoute`.
|
|
1051
|
+
|
|
1052
|
+
Here is a native HTTP mutation for `post.like`:
|
|
1053
|
+
|
|
1054
|
+
```tsx
|
|
1055
|
+
export const fate = createFateServer({
|
|
1056
|
+
mutations: {
|
|
1057
|
+
'post.like': {
|
|
1058
|
+
input: likeInput,
|
|
1059
|
+
resolve: async ({ ctx, input, select }) => {
|
|
1060
|
+
await ctx.prisma.post.update({
|
|
1061
|
+
data: {
|
|
1062
|
+
likes: {
|
|
1063
|
+
increment: 1,
|
|
1064
|
+
},
|
|
1065
|
+
},
|
|
1066
|
+
where: { id: input.id },
|
|
1067
|
+
});
|
|
1068
|
+
|
|
1069
|
+
return sources.resolveById({
|
|
1070
|
+
ctx,
|
|
1071
|
+
id: input.id,
|
|
1072
|
+
input: { select },
|
|
1073
|
+
view: postDataView,
|
|
1074
|
+
});
|
|
1075
|
+
},
|
|
1076
|
+
type: 'Post',
|
|
1077
|
+
},
|
|
1078
|
+
},
|
|
1079
|
+
roots: Root,
|
|
1080
|
+
sources,
|
|
1081
|
+
});
|
|
1082
|
+
```
|
|
1083
|
+
|
|
1084
|
+
The equivalent tRPC mutation lives in your router and returns the selected shape that the client asked for:
|
|
1030
1085
|
|
|
1031
1086
|
```tsx
|
|
1032
1087
|
import { z } from 'zod';
|
|
@@ -1065,7 +1120,7 @@ export const postRouter = router({
|
|
|
1065
1120
|
});
|
|
1066
1121
|
```
|
|
1067
1122
|
|
|
1068
|
-
See
|
|
1123
|
+
See [Server Integration](/docs/guide/server-integration.md) for complete native HTTP and tRPC setup examples, and [Void Integration](/docs/guide/void-integration.md) for route helpers when your app runs on Void.
|
|
1069
1124
|
|
|
1070
1125
|
### Action & Mutation Error Handling
|
|
1071
1126
|
|
|
@@ -1148,9 +1203,9 @@ addComment({
|
|
|
1148
1203
|
|
|
1149
1204
|
## Server Integration
|
|
1150
1205
|
|
|
1151
|
-
Until now, we have focused on the client-side API of fate. You'll need a backend that follows fate's data protocol so the Vite plugin can
|
|
1206
|
+
Until now, we have focused on the client-side API of fate. You'll need a backend that follows fate's data protocol so the Vite plugin can wire the typed fate APIs into your app. _fate_ currently ships two server paths:
|
|
1152
1207
|
|
|
1153
|
-
- The native
|
|
1208
|
+
- The native fate protocol, which is transport-agnostic and can be hosted by any Fetch-compatible server.
|
|
1154
1209
|
- The tRPC adapter, which keeps compatibility with existing tRPC backends.
|
|
1155
1210
|
|
|
1156
1211
|
_fate_ currently provides database adapters for Prisma and Drizzle, but the framework itself is not coupled to a particular ORM. The adapters plug into the same source execution runtime and can be exposed through the native protocol or through tRPC.
|
|
@@ -1164,7 +1219,7 @@ fate expects that data is served by a backend that follows these conventions:
|
|
|
1164
1219
|
|
|
1165
1220
|
Objects are identified by their ID and type name (`__typename`, e.g. `Post`, `User`), and stored by `__typename:id` (e.g. "Post:123") in the client cache. fate keeps list orderings under stable keys derived from the backend procedure and args. Relations are stored as IDs and returned to components as ViewRef tokens.
|
|
1166
1221
|
|
|
1167
|
-
fate's type definitions might seem verbose at first glance. However, with fate's minimal API surface, AI tools can easily
|
|
1222
|
+
fate's type definitions might seem verbose at first glance. However, with fate's minimal API surface, AI tools can easily write this code for you, and the Vite plugin takes care of connecting it to your app.
|
|
1168
1223
|
|
|
1169
1224
|
> [!NOTE]
|
|
1170
1225
|
> You can adopt _fate_ incrementally in an existing tRPC codebase without changing your existing schema by adding these queries alongside your existing procedures.
|
|
@@ -1260,9 +1315,9 @@ export const Root = {
|
|
|
1260
1315
|
};
|
|
1261
1316
|
```
|
|
1262
1317
|
|
|
1263
|
-
Entries that wrap their view in `list(...)` are treated as list resolvers. In the native protocol, the root key is the operation name
|
|
1318
|
+
Entries that wrap their view in `list(...)` are treated as list resolvers. In the native protocol, the root key is the operation name used by the client. In the tRPC adapter, `procedure` can point that root at a specific router procedure. If you omit `list(...)`, fate treats the entry as a standard query.
|
|
1264
1319
|
|
|
1265
|
-
You can pass default list options such as `orderBy` to `list(...)`. Ordering is scoped to that specific list wrapper: `Root.posts` can order posts by `createdAt desc`, while `categoryDataView.posts` or `postDataView.comments` can choose their own order. If no order is provided,
|
|
1320
|
+
You can pass default list options such as `orderBy` to `list(...)`. Ordering is scoped to that specific list wrapper: `Root.posts` can order posts by `createdAt desc`, while `categoryDataView.posts` or `postDataView.comments` can choose their own order. If no order is provided, fate orders by `id asc`. fate always appends `id asc` as a tie-breaker when no `id` order is present; include `id` yourself when you need a different tie-breaker direction such as `id desc`. Use the array form when ordering by multiple fields so the priority is unambiguous.
|
|
1266
1321
|
|
|
1267
1322
|
For the above `Root` definitions, you can make the following requests using `useRequest`:
|
|
1268
1323
|
|
|
@@ -1283,7 +1338,7 @@ const { posts, categories, viewer } = useRequest({
|
|
|
1283
1338
|
});
|
|
1284
1339
|
```
|
|
1285
1340
|
|
|
1286
|
-
### Native
|
|
1341
|
+
### Native fate protocol
|
|
1287
1342
|
|
|
1288
1343
|
The native protocol keeps tRPC optional. Create a source adapter from your ORM integration, pass it to `createFateServer`, and expose the returned server through a Fetch-compatible handler.
|
|
1289
1344
|
|
|
@@ -1346,7 +1401,7 @@ export default defineConfig({
|
|
|
1346
1401
|
});
|
|
1347
1402
|
```
|
|
1348
1403
|
|
|
1349
|
-
|
|
1404
|
+
With the native transport, the Vite plugin handles the HTTP transport setup. If you need to create a client manually, use `createFateClient` with the same route:
|
|
1350
1405
|
|
|
1351
1406
|
```tsx
|
|
1352
1407
|
import { createFateClient } from 'react-fate/client';
|
|
@@ -1356,7 +1411,7 @@ const client = createFateClient({
|
|
|
1356
1411
|
});
|
|
1357
1412
|
```
|
|
1358
1413
|
|
|
1359
|
-
The HTTP transport batches operations issued in the same microtask into one `POST /fate` request. Live views use one `GET /fate/live` SSE stream per
|
|
1414
|
+
The HTTP transport batches operations issued in the same microtask into one `POST /fate` request. Live views use one `GET /fate/live` SSE stream per fate client and `POST /fate/live` control messages when views subscribe or unsubscribe.
|
|
1360
1415
|
|
|
1361
1416
|
#### Custom Queries
|
|
1362
1417
|
|
|
@@ -1445,15 +1500,17 @@ live.update('Post', post.id, {
|
|
|
1445
1500
|
|
|
1446
1501
|
`changed` is optional. When provided, fate resolves only the changed fields selected by each live subscription and skips subscriptions that do not select those fields. `createLiveEventBus` is an in-memory fanout bus. It forwards `eventId` to SSE clients, but it does not replay events after reconnects. If your app needs lossless reconnect behavior, provide a durable live bus implementation that uses the `lastEventId` passed to `listen`, `listenConnection`, `subscribe`, and `subscribeConnection`.
|
|
1447
1502
|
|
|
1448
|
-
|
|
1503
|
+
Native SSE connections keep a bounded in-memory queue while events are waiting to be resolved and sent. The default is `1000` queued events per connection. If a client falls behind and exceeds that limit, fate closes the live connection instead of buffering indefinitely. Configure it with `live: { bus: live, maxQueueSize: 500 }`.
|
|
1504
|
+
|
|
1505
|
+
### tRPC fate setup
|
|
1449
1506
|
|
|
1450
1507
|
The Prisma and Drizzle tRPC integrations connect your data views to your database, bind fate's standard tRPC procedures, and expose helpers for custom queries and mutations.
|
|
1451
1508
|
|
|
1452
|
-
Pass the `Root` export from `views.ts` to
|
|
1509
|
+
Pass the `Root` export from `views.ts` to fate in your tRPC `init.ts` file. fate walks that view graph to find the data views it needs. `id` defaults to `"id"`, and fate uses it as the fallback ordering for cursor pagination. Relations are inferred from the data view and ORM schema: a nested data view is loaded as a singular relation, `list(view)` is loaded as a list relation, and Drizzle join tables are discovered from relation metadata.
|
|
1453
1510
|
|
|
1454
1511
|
#### Prisma
|
|
1455
1512
|
|
|
1456
|
-
Use `createPrismaFate` from `@nkzw/fate/server/prisma` next to your tRPC helpers. By default,
|
|
1513
|
+
Use `createPrismaFate` from `@nkzw/fate/server/prisma` next to your tRPC helpers. By default, fate reads Prisma delegates from `ctx.prisma` using each data view's type name:
|
|
1457
1514
|
|
|
1458
1515
|
```tsx
|
|
1459
1516
|
import { initTRPC } from '@trpc/server';
|
|
@@ -1502,7 +1559,7 @@ return plan.resolve(post);
|
|
|
1502
1559
|
|
|
1503
1560
|
#### Drizzle
|
|
1504
1561
|
|
|
1505
|
-
Use `createDrizzleFate` from `@nkzw/fate/server/drizzle`.
|
|
1562
|
+
Use `createDrizzleFate` from `@nkzw/fate/server/drizzle`. fate matches data view type names to Drizzle tables from your schema. The `db` option can be a Drizzle database object or a function that receives your tRPC context and returns a request-scoped database object:
|
|
1506
1563
|
|
|
1507
1564
|
```tsx
|
|
1508
1565
|
import { initTRPC } from '@trpc/server';
|
|
@@ -1541,9 +1598,21 @@ export const fate = createDrizzleFate<AppContext, typeof procedure>({
|
|
|
1541
1598
|
|
|
1542
1599
|
The Drizzle adapter builds SQL queries from your registered data views. It selects only requested columns, hydrates singular, list, and many-to-many relations, supports nested cursor pagination, and hydrates computed `count(...)` dependencies with SQL grouped counts. Count filters may be plain equality objects or Drizzle SQL predicates written as `(columns) => eq(columns.status, 'GOING')`.
|
|
1543
1600
|
|
|
1601
|
+
Nested paginated relations are resolved with one child-page query per parent row. fate runs those child queries with a default concurrency limit of `10` so a single request cannot flood the database connection pool. Tune this with `nestedPaginationConcurrency` if your database pool or workload needs a different limit:
|
|
1602
|
+
|
|
1603
|
+
```tsx
|
|
1604
|
+
export const fate = createDrizzleFate<AppContext, typeof procedure>({
|
|
1605
|
+
db,
|
|
1606
|
+
nestedPaginationConcurrency: 5,
|
|
1607
|
+
procedure,
|
|
1608
|
+
schema,
|
|
1609
|
+
views: Root,
|
|
1610
|
+
});
|
|
1611
|
+
```
|
|
1612
|
+
|
|
1544
1613
|
For request-specific sorting, prefer a custom root query that validates and translates explicit sort args.
|
|
1545
1614
|
|
|
1546
|
-
For many-to-many relations, define the join table relations in your Drizzle schema.
|
|
1615
|
+
For many-to-many relations, define the join table relations in your Drizzle schema. fate discovers a join table that points at both the source table and the target table:
|
|
1547
1616
|
|
|
1548
1617
|
```tsx
|
|
1549
1618
|
export const fate = createDrizzleFate<AppContext, typeof procedure>({
|
|
@@ -1601,7 +1670,7 @@ return post;
|
|
|
1601
1670
|
|
|
1602
1671
|
### tRPC Procedures
|
|
1603
1672
|
|
|
1604
|
-
Use `fate.procedures` to build the standard `byId` and `list` procedures expected by
|
|
1673
|
+
Use `fate.procedures` to build the standard `byId` and `list` procedures expected by fate's request APIs:
|
|
1605
1674
|
|
|
1606
1675
|
```tsx
|
|
1607
1676
|
import { fate, router } from '../init.ts';
|
|
@@ -1695,11 +1764,11 @@ export const postDataView = dataView<PostItem>('Post')({
|
|
|
1695
1764
|
|
|
1696
1765
|
The adapters fetch the hidden `field(...)` and `count(...)` dependencies for you. This keeps private fields like `email` available to the resolver without exposing them to the client selection.
|
|
1697
1766
|
|
|
1698
|
-
###
|
|
1767
|
+
### Connecting the Client
|
|
1699
1768
|
|
|
1700
|
-
Now that we have defined our client views and our
|
|
1769
|
+
Now that we have defined our client views and our server module, add fate's Vite plugin to the client app. The plugin reads your server exports and wires the typed fate APIs into your app.
|
|
1701
1770
|
|
|
1702
|
-
|
|
1771
|
+
For tRPC, make sure the `router.ts` file exports the `appRouter` object, `AppRouter` type and all the views we have defined:
|
|
1703
1772
|
|
|
1704
1773
|
```tsx
|
|
1705
1774
|
import { router } from './init.ts';
|
|
@@ -1731,11 +1800,11 @@ export default defineConfig({
|
|
|
1731
1800
|
});
|
|
1732
1801
|
```
|
|
1733
1802
|
|
|
1734
|
-
_Note: fate uses the specified server module name to
|
|
1803
|
+
_Note: fate uses the specified server module name to find the server types it needs. Make sure that the module is available to the client package's Vite config._
|
|
1735
1804
|
|
|
1736
|
-
During development, the plugin watches the server module and the files it imports. When one of those files changes, fate
|
|
1805
|
+
During development, the plugin watches the server module and the files it imports. When one of those files changes, fate updates the internal client wiring and invalidates `@nkzw/fate/client` in Vite's module graph.
|
|
1737
1806
|
|
|
1738
|
-
For a barebones client without React, import the plugin from `@nkzw/fate/vite` and the
|
|
1807
|
+
For a barebones client without React, import the plugin from `@nkzw/fate/vite` and the client APIs from `@nkzw/fate/client`. The plugin wires the same server types for the selected import path.
|
|
1739
1808
|
|
|
1740
1809
|
The plugin writes project-local types under `.fate/`. If your TypeScript config does not already include dot-directories, extend the generated config:
|
|
1741
1810
|
|
|
@@ -1747,7 +1816,7 @@ The plugin writes project-local types under `.fate/`. If your TypeScript config
|
|
|
1747
1816
|
|
|
1748
1817
|
### Creating a _fate_ Client
|
|
1749
1818
|
|
|
1750
|
-
Now that the Vite plugin
|
|
1819
|
+
Now that the Vite plugin has connected the types, create a fate client instance and provide it to your React app with the `FateClient` context provider:
|
|
1751
1820
|
|
|
1752
1821
|
```tsx
|
|
1753
1822
|
import { httpBatchLink } from '@trpc/client';
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> **FateClient**(`__namedParameters`): `Element`
|
|
4
4
|
|
|
5
|
-
Defined in: [packages/react-fate/src/context.tsx:17](https://github.com/nkzw-tech/fate/blob/
|
|
5
|
+
Defined in: [packages/react-fate/src/context.tsx:17](https://github.com/nkzw-tech/fate/blob/641aeb0396b93477dc2e75fdb2c29a4c2b62a272/packages/react-fate/src/context.tsx#L17)
|
|
6
6
|
|
|
7
7
|
Provider component that supplies a configured `FateClient` to React hooks.
|
|
8
8
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> **useFateClient**\<`T`\>(): `FateClient`\<`T`\[`0`\], `T`\[`1`\]\>
|
|
4
4
|
|
|
5
|
-
Defined in: [packages/react-fate/src/context.tsx:30](https://github.com/nkzw-tech/fate/blob/
|
|
5
|
+
Defined in: [packages/react-fate/src/context.tsx:30](https://github.com/nkzw-tech/fate/blob/641aeb0396b93477dc2e75fdb2c29a4c2b62a272/packages/react-fate/src/context.tsx#L30)
|
|
6
6
|
|
|
7
7
|
Returns the nearest `FateClient` from context.
|
|
8
8
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> **useListView**\<`C`\>(`selection`, `connection`): \[`ConnectionItems`\<`NonNullable`\<`C`\>\>, `LoadMoreFn` \| `null`, `LoadMoreFn` \| `null`\]
|
|
4
4
|
|
|
5
|
-
Defined in: [packages/react-fate/src/useListView.tsx:16](https://github.com/nkzw-tech/fate/blob/
|
|
5
|
+
Defined in: [packages/react-fate/src/useListView.tsx:16](https://github.com/nkzw-tech/fate/blob/641aeb0396b93477dc2e75fdb2c29a4c2b62a272/packages/react-fate/src/useListView.tsx#L16)
|
|
6
6
|
|
|
7
7
|
Subscribes to a connection field, returning the current items and pagination
|
|
8
8
|
helpers to load the next or previous page.
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> **useLiveListView**\<`C`\>(`selection`, `connection`): \[`ConnectionItems`\<`NonNullable`\<`C`\>\>, `LoadMoreFn` \| `null`, `LoadMoreFn` \| `null`\]
|
|
4
4
|
|
|
5
|
-
Defined in: [packages/react-fate/src/useLiveListView.tsx:16](https://github.com/nkzw-tech/fate/blob/
|
|
5
|
+
Defined in: [packages/react-fate/src/useLiveListView.tsx:16](https://github.com/nkzw-tech/fate/blob/641aeb0396b93477dc2e75fdb2c29a4c2b62a272/packages/react-fate/src/useLiveListView.tsx#L16)
|
|
6
6
|
|
|
7
7
|
Subscribes to a connection field, returning live-updating items and pagination
|
|
8
8
|
helpers to load the next or previous page.
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> **useLiveView**\<`V`, `R`\>(`view`, `ref`): `R` *extends* `null` ? `null` : `Readonly`\<`ViewSelection`\<`V`\> *extends* `Selection`\<`ViewEntityWithTypename`\<`V`\>\> ? `Mask`\<`ViewEntityWithTypename`\<`V`\>, \{ \[K in string \| number \| symbol as K extends "\_\_typename" ? never : K\]?: SelectionFieldValue\<ViewEntityWithTypename\<V\>, K\> \} & `object` & `SelectionViewSpread`\<`ViewEntityWithTypename`\<`V`\>\> & `ViewSelection`\<`V`\>\> : `ViewEntityWithTypename`\<`V`\> & `object`\>
|
|
4
4
|
|
|
5
|
-
Defined in: [packages/react-fate/src/useLiveView.tsx:17](https://github.com/nkzw-tech/fate/blob/
|
|
5
|
+
Defined in: [packages/react-fate/src/useLiveView.tsx:17](https://github.com/nkzw-tech/fate/blob/641aeb0396b93477dc2e75fdb2c29a4c2b62a272/packages/react-fate/src/useLiveView.tsx#L17)
|
|
6
6
|
|
|
7
7
|
Resolves a reference against a view and subscribes to live server updates for
|
|
8
8
|
that selection.
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> **useRequest**\<`R`, `O`\>(`request`, `options?`): `RequestResult`\<`O`, `R`\>
|
|
4
4
|
|
|
5
|
-
Defined in: [packages/react-fate/src/useRequest.tsx:25](https://github.com/nkzw-tech/fate/blob/
|
|
5
|
+
Defined in: [packages/react-fate/src/useRequest.tsx:25](https://github.com/nkzw-tech/fate/blob/641aeb0396b93477dc2e75fdb2c29a4c2b62a272/packages/react-fate/src/useRequest.tsx#L25)
|
|
6
6
|
|
|
7
7
|
Declares the data a screen needs and kicks off fetching, suspending while the
|
|
8
8
|
request resolves.
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> **useView**\<`V`, `R`\>(`view`, `ref`): `R` *extends* `null` ? `null` : `Readonly`\<`ViewSelection`\<`V`\> *extends* `Selection`\<`ViewEntityWithTypename`\<`V`\>\> ? `Mask`\<`ViewEntityWithTypename`\<`V`\>, \{ \[K in string \| number \| symbol as K extends "\_\_typename" ? never : K\]?: SelectionFieldValue\<ViewEntityWithTypename\<V\>, K\> \} & `object` & `SelectionViewSpread`\<`ViewEntityWithTypename`\<`V`\>\> & `ViewSelection`\<`V`\>\> : `ViewEntityWithTypename`\<`V`\> & `object`\>
|
|
4
4
|
|
|
5
|
-
Defined in: [packages/react-fate/src/useView.tsx:37](https://github.com/nkzw-tech/fate/blob/
|
|
5
|
+
Defined in: [packages/react-fate/src/useView.tsx:37](https://github.com/nkzw-tech/fate/blob/641aeb0396b93477dc2e75fdb2c29a4c2b62a272/packages/react-fate/src/useView.tsx#L37)
|
|
6
6
|
|
|
7
7
|
Resolves a reference against a view and subscribes to updates for that selection.
|
|
8
8
|
|
package/docs/guide/actions.md
CHANGED
|
@@ -5,9 +5,13 @@ fate does not provide hooks for mutations like traditional data fetching librari
|
|
|
5
5
|
- `fate.actions` for use with [`useActionState`](https://react.dev/reference/react/useActionState) and React Actions.
|
|
6
6
|
- `fate.mutations` for traditional imperative mutation calls.
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
Server mutations are exposed automatically as actions and mutations by fate's Vite plugin. The transport determines where those mutations are declared:
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
- With the [native HTTP transport](server-integration.md#native-fate-protocol), mutations come from the `mutations` object passed to `createFateServer`.
|
|
11
|
+
- With the [tRPC adapter](server-integration.md#trpc-fate-setup), mutations come from tRPC mutation procedures exposed through your fate-enabled router.
|
|
12
|
+
- With [Void](void-integration.md), mutations use the same native fate server shape and are exposed through the Void route helpers.
|
|
13
|
+
|
|
14
|
+
If you have a mutation named `post.like`, a `LikeButton` component using fate Actions and an async component library could look like this:
|
|
11
15
|
|
|
12
16
|
```tsx
|
|
13
17
|
import { useActionState } from 'react';
|
|
@@ -142,7 +146,45 @@ You can call mutations from anywhere, and without waiting for previous mutations
|
|
|
142
146
|
|
|
143
147
|
## Mutation Server Implementation
|
|
144
148
|
|
|
145
|
-
fate Actions & Mutations are backed by regular
|
|
149
|
+
fate Actions & Mutations are backed by regular server mutations. If you already know how your fate server is wired, the client-side API above is the same regardless of transport. If not, start with the server setup for your environment:
|
|
150
|
+
|
|
151
|
+
- [Native HTTP custom mutations](server-integration.md#custom-mutations) use `createFateServer({ mutations })`.
|
|
152
|
+
- [tRPC fate setup](server-integration.md#trpc-fate-setup) wires fate into your tRPC router; custom writes can use the same `fate.createPlan` and `fate.resolveById` helpers shown there.
|
|
153
|
+
- [Void integration](void-integration.md) exposes a native fate server from Void routes; define mutations with the native `createFateServer({ mutations })` API and serve them through `defineVoidFateRoute`.
|
|
154
|
+
|
|
155
|
+
Here is a native HTTP mutation for `post.like`:
|
|
156
|
+
|
|
157
|
+
```tsx
|
|
158
|
+
export const fate = createFateServer({
|
|
159
|
+
mutations: {
|
|
160
|
+
'post.like': {
|
|
161
|
+
input: likeInput,
|
|
162
|
+
resolve: async ({ ctx, input, select }) => {
|
|
163
|
+
await ctx.prisma.post.update({
|
|
164
|
+
data: {
|
|
165
|
+
likes: {
|
|
166
|
+
increment: 1,
|
|
167
|
+
},
|
|
168
|
+
},
|
|
169
|
+
where: { id: input.id },
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
return sources.resolveById({
|
|
173
|
+
ctx,
|
|
174
|
+
id: input.id,
|
|
175
|
+
input: { select },
|
|
176
|
+
view: postDataView,
|
|
177
|
+
});
|
|
178
|
+
},
|
|
179
|
+
type: 'Post',
|
|
180
|
+
},
|
|
181
|
+
},
|
|
182
|
+
roots: Root,
|
|
183
|
+
sources,
|
|
184
|
+
});
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
The equivalent tRPC mutation lives in your router and returns the selected shape that the client asked for:
|
|
146
188
|
|
|
147
189
|
```tsx
|
|
148
190
|
import { z } from 'zod';
|
|
@@ -181,7 +223,7 @@ export const postRouter = router({
|
|
|
181
223
|
});
|
|
182
224
|
```
|
|
183
225
|
|
|
184
|
-
See
|
|
226
|
+
See [Server Integration](server-integration.md) for complete native HTTP and tRPC setup examples, and [Void Integration](void-integration.md) for route helpers when your app runs on Void.
|
|
185
227
|
|
|
186
228
|
## Action & Mutation Error Handling
|
|
187
229
|
|
package/docs/guide/live-views.md
CHANGED
|
@@ -22,7 +22,7 @@ The API mirrors `useView`: pass a view and a ref, and get back the same masked d
|
|
|
22
22
|
|
|
23
23
|
## How Live Updates Work
|
|
24
24
|
|
|
25
|
-
The native HTTP transport opens one Server-Sent Events (SSE) connection per
|
|
25
|
+
The native HTTP transport opens one Server-Sent Events (SSE) connection per fate client. When components mount or unmount live views, the client sends subscribe and unsubscribe control messages to the server. The server keeps those selections on the connection and sends updates only for records that connection subscribed to.
|
|
26
26
|
|
|
27
27
|
When the server sends an update, fate normalizes the selected record into the same cache used by requests, actions, and mutations. Components that read affected fields re-render automatically.
|
|
28
28
|
|
|
@@ -32,7 +32,7 @@ Live deletion events remove the record from the normalized cache in the same way
|
|
|
32
32
|
|
|
33
33
|
## Client Setup
|
|
34
34
|
|
|
35
|
-
|
|
35
|
+
Configure the native transport and point the client at your fate endpoint:
|
|
36
36
|
|
|
37
37
|
```tsx
|
|
38
38
|
import { FateClient } from 'react-fate';
|
|
@@ -83,6 +83,19 @@ export const fate = createFateServer<AppContext>({
|
|
|
83
83
|
app.all('/fate/*', createHonoFateHandler(fate));
|
|
84
84
|
```
|
|
85
85
|
|
|
86
|
+
fate keeps a bounded in-memory queue for each native SSE connection while live events are waiting to be resolved and sent. The default limit is `1000` queued events per connection. If a client falls behind and exceeds the limit, fate closes that live connection so server memory cannot grow without bound. You can tune the limit by passing the object form:
|
|
87
|
+
|
|
88
|
+
```tsx
|
|
89
|
+
export const fate = createFateServer<AppContext>({
|
|
90
|
+
live: {
|
|
91
|
+
bus: live,
|
|
92
|
+
maxQueueSize: 500,
|
|
93
|
+
},
|
|
94
|
+
roots: Root,
|
|
95
|
+
sources,
|
|
96
|
+
});
|
|
97
|
+
```
|
|
98
|
+
|
|
86
99
|
Once this is in place, components can switch from `useView` to `useLiveView` without changing their view definitions or return types.
|
|
87
100
|
|
|
88
101
|
## Live List Views
|
package/docs/guide/requests.md
CHANGED
|
@@ -26,7 +26,7 @@ This component suspends or throws errors, which bubble up to the nearest error b
|
|
|
26
26
|
|
|
27
27
|
> [!NOTE]
|
|
28
28
|
>
|
|
29
|
-
> `useRequest`
|
|
29
|
+
> `useRequest` may issue multiple operations in the same render pass. fate transports can batch those operations into fewer network requests: the native HTTP transport batches same-microtask operations into one `POST /fate` request, and the tRPC adapter can use tRPC's [HTTP Batch Link](https://trpc.io/docs/client/links/httpBatchLink).
|
|
30
30
|
|
|
31
31
|
## Requesting Objects by ID
|
|
32
32
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# Server Integration
|
|
2
2
|
|
|
3
|
-
Until now, we have focused on the client-side API of fate. You'll need a backend that follows fate's data protocol so the Vite plugin can
|
|
3
|
+
Until now, we have focused on the client-side API of fate. You'll need a backend that follows fate's data protocol so the Vite plugin can wire the typed fate APIs into your app. _fate_ currently ships two server paths:
|
|
4
4
|
|
|
5
|
-
- The native
|
|
5
|
+
- The native fate protocol, which is transport-agnostic and can be hosted by any Fetch-compatible server.
|
|
6
6
|
- The tRPC adapter, which keeps compatibility with existing tRPC backends.
|
|
7
7
|
|
|
8
8
|
_fate_ currently provides database adapters for Prisma and Drizzle, but the framework itself is not coupled to a particular ORM. The adapters plug into the same source execution runtime and can be exposed through the native protocol or through tRPC.
|
|
@@ -16,7 +16,7 @@ fate expects that data is served by a backend that follows these conventions:
|
|
|
16
16
|
|
|
17
17
|
Objects are identified by their ID and type name (`__typename`, e.g. `Post`, `User`), and stored by `__typename:id` (e.g. "Post:123") in the client cache. fate keeps list orderings under stable keys derived from the backend procedure and args. Relations are stored as IDs and returned to components as ViewRef tokens.
|
|
18
18
|
|
|
19
|
-
fate's type definitions might seem verbose at first glance. However, with fate's minimal API surface, AI tools can easily
|
|
19
|
+
fate's type definitions might seem verbose at first glance. However, with fate's minimal API surface, AI tools can easily write this code for you, and the Vite plugin takes care of connecting it to your app.
|
|
20
20
|
|
|
21
21
|
> [!NOTE]
|
|
22
22
|
> You can adopt _fate_ incrementally in an existing tRPC codebase without changing your existing schema by adding these queries alongside your existing procedures.
|
|
@@ -112,9 +112,9 @@ export const Root = {
|
|
|
112
112
|
};
|
|
113
113
|
```
|
|
114
114
|
|
|
115
|
-
Entries that wrap their view in `list(...)` are treated as list resolvers. In the native protocol, the root key is the operation name
|
|
115
|
+
Entries that wrap their view in `list(...)` are treated as list resolvers. In the native protocol, the root key is the operation name used by the client. In the tRPC adapter, `procedure` can point that root at a specific router procedure. If you omit `list(...)`, fate treats the entry as a standard query.
|
|
116
116
|
|
|
117
|
-
You can pass default list options such as `orderBy` to `list(...)`. Ordering is scoped to that specific list wrapper: `Root.posts` can order posts by `createdAt desc`, while `categoryDataView.posts` or `postDataView.comments` can choose their own order. If no order is provided,
|
|
117
|
+
You can pass default list options such as `orderBy` to `list(...)`. Ordering is scoped to that specific list wrapper: `Root.posts` can order posts by `createdAt desc`, while `categoryDataView.posts` or `postDataView.comments` can choose their own order. If no order is provided, fate orders by `id asc`. fate always appends `id asc` as a tie-breaker when no `id` order is present; include `id` yourself when you need a different tie-breaker direction such as `id desc`. Use the array form when ordering by multiple fields so the priority is unambiguous.
|
|
118
118
|
|
|
119
119
|
For the above `Root` definitions, you can make the following requests using `useRequest`:
|
|
120
120
|
|
|
@@ -135,7 +135,7 @@ const { posts, categories, viewer } = useRequest({
|
|
|
135
135
|
});
|
|
136
136
|
```
|
|
137
137
|
|
|
138
|
-
## Native
|
|
138
|
+
## Native fate protocol
|
|
139
139
|
|
|
140
140
|
The native protocol keeps tRPC optional. Create a source adapter from your ORM integration, pass it to `createFateServer`, and expose the returned server through a Fetch-compatible handler.
|
|
141
141
|
|
|
@@ -198,7 +198,7 @@ export default defineConfig({
|
|
|
198
198
|
});
|
|
199
199
|
```
|
|
200
200
|
|
|
201
|
-
|
|
201
|
+
With the native transport, the Vite plugin handles the HTTP transport setup. If you need to create a client manually, use `createFateClient` with the same route:
|
|
202
202
|
|
|
203
203
|
```tsx
|
|
204
204
|
import { createFateClient } from 'react-fate/client';
|
|
@@ -208,7 +208,7 @@ const client = createFateClient({
|
|
|
208
208
|
});
|
|
209
209
|
```
|
|
210
210
|
|
|
211
|
-
The HTTP transport batches operations issued in the same microtask into one `POST /fate` request. Live views use one `GET /fate/live` SSE stream per
|
|
211
|
+
The HTTP transport batches operations issued in the same microtask into one `POST /fate` request. Live views use one `GET /fate/live` SSE stream per fate client and `POST /fate/live` control messages when views subscribe or unsubscribe.
|
|
212
212
|
|
|
213
213
|
### Custom Queries
|
|
214
214
|
|
|
@@ -297,15 +297,17 @@ live.update('Post', post.id, {
|
|
|
297
297
|
|
|
298
298
|
`changed` is optional. When provided, fate resolves only the changed fields selected by each live subscription and skips subscriptions that do not select those fields. `createLiveEventBus` is an in-memory fanout bus. It forwards `eventId` to SSE clients, but it does not replay events after reconnects. If your app needs lossless reconnect behavior, provide a durable live bus implementation that uses the `lastEventId` passed to `listen`, `listenConnection`, `subscribe`, and `subscribeConnection`.
|
|
299
299
|
|
|
300
|
-
|
|
300
|
+
Native SSE connections keep a bounded in-memory queue while events are waiting to be resolved and sent. The default is `1000` queued events per connection. If a client falls behind and exceeds that limit, fate closes the live connection instead of buffering indefinitely. Configure it with `live: { bus: live, maxQueueSize: 500 }`.
|
|
301
|
+
|
|
302
|
+
## tRPC fate setup
|
|
301
303
|
|
|
302
304
|
The Prisma and Drizzle tRPC integrations connect your data views to your database, bind fate's standard tRPC procedures, and expose helpers for custom queries and mutations.
|
|
303
305
|
|
|
304
|
-
Pass the `Root` export from `views.ts` to
|
|
306
|
+
Pass the `Root` export from `views.ts` to fate in your tRPC `init.ts` file. fate walks that view graph to find the data views it needs. `id` defaults to `"id"`, and fate uses it as the fallback ordering for cursor pagination. Relations are inferred from the data view and ORM schema: a nested data view is loaded as a singular relation, `list(view)` is loaded as a list relation, and Drizzle join tables are discovered from relation metadata.
|
|
305
307
|
|
|
306
308
|
### Prisma
|
|
307
309
|
|
|
308
|
-
Use `createPrismaFate` from `@nkzw/fate/server/prisma` next to your tRPC helpers. By default,
|
|
310
|
+
Use `createPrismaFate` from `@nkzw/fate/server/prisma` next to your tRPC helpers. By default, fate reads Prisma delegates from `ctx.prisma` using each data view's type name:
|
|
309
311
|
|
|
310
312
|
```tsx
|
|
311
313
|
import { initTRPC } from '@trpc/server';
|
|
@@ -354,7 +356,7 @@ return plan.resolve(post);
|
|
|
354
356
|
|
|
355
357
|
### Drizzle
|
|
356
358
|
|
|
357
|
-
Use `createDrizzleFate` from `@nkzw/fate/server/drizzle`.
|
|
359
|
+
Use `createDrizzleFate` from `@nkzw/fate/server/drizzle`. fate matches data view type names to Drizzle tables from your schema. The `db` option can be a Drizzle database object or a function that receives your tRPC context and returns a request-scoped database object:
|
|
358
360
|
|
|
359
361
|
```tsx
|
|
360
362
|
import { initTRPC } from '@trpc/server';
|
|
@@ -393,9 +395,21 @@ export const fate = createDrizzleFate<AppContext, typeof procedure>({
|
|
|
393
395
|
|
|
394
396
|
The Drizzle adapter builds SQL queries from your registered data views. It selects only requested columns, hydrates singular, list, and many-to-many relations, supports nested cursor pagination, and hydrates computed `count(...)` dependencies with SQL grouped counts. Count filters may be plain equality objects or Drizzle SQL predicates written as `(columns) => eq(columns.status, 'GOING')`.
|
|
395
397
|
|
|
398
|
+
Nested paginated relations are resolved with one child-page query per parent row. fate runs those child queries with a default concurrency limit of `10` so a single request cannot flood the database connection pool. Tune this with `nestedPaginationConcurrency` if your database pool or workload needs a different limit:
|
|
399
|
+
|
|
400
|
+
```tsx
|
|
401
|
+
export const fate = createDrizzleFate<AppContext, typeof procedure>({
|
|
402
|
+
db,
|
|
403
|
+
nestedPaginationConcurrency: 5,
|
|
404
|
+
procedure,
|
|
405
|
+
schema,
|
|
406
|
+
views: Root,
|
|
407
|
+
});
|
|
408
|
+
```
|
|
409
|
+
|
|
396
410
|
For request-specific sorting, prefer a custom root query that validates and translates explicit sort args.
|
|
397
411
|
|
|
398
|
-
For many-to-many relations, define the join table relations in your Drizzle schema.
|
|
412
|
+
For many-to-many relations, define the join table relations in your Drizzle schema. fate discovers a join table that points at both the source table and the target table:
|
|
399
413
|
|
|
400
414
|
```tsx
|
|
401
415
|
export const fate = createDrizzleFate<AppContext, typeof procedure>({
|
|
@@ -453,7 +467,7 @@ return post;
|
|
|
453
467
|
|
|
454
468
|
## tRPC Procedures
|
|
455
469
|
|
|
456
|
-
Use `fate.procedures` to build the standard `byId` and `list` procedures expected by
|
|
470
|
+
Use `fate.procedures` to build the standard `byId` and `list` procedures expected by fate's request APIs:
|
|
457
471
|
|
|
458
472
|
```tsx
|
|
459
473
|
import { fate, router } from '../init.ts';
|
|
@@ -547,11 +561,11 @@ export const postDataView = dataView<PostItem>('Post')({
|
|
|
547
561
|
|
|
548
562
|
The adapters fetch the hidden `field(...)` and `count(...)` dependencies for you. This keeps private fields like `email` available to the resolver without exposing them to the client selection.
|
|
549
563
|
|
|
550
|
-
##
|
|
564
|
+
## Connecting the Client
|
|
551
565
|
|
|
552
|
-
Now that we have defined our client views and our
|
|
566
|
+
Now that we have defined our client views and our server module, add fate's Vite plugin to the client app. The plugin reads your server exports and wires the typed fate APIs into your app.
|
|
553
567
|
|
|
554
|
-
|
|
568
|
+
For tRPC, make sure the `router.ts` file exports the `appRouter` object, `AppRouter` type and all the views we have defined:
|
|
555
569
|
|
|
556
570
|
```tsx
|
|
557
571
|
import { router } from './init.ts';
|
|
@@ -583,11 +597,11 @@ export default defineConfig({
|
|
|
583
597
|
});
|
|
584
598
|
```
|
|
585
599
|
|
|
586
|
-
_Note: fate uses the specified server module name to
|
|
600
|
+
_Note: fate uses the specified server module name to find the server types it needs. Make sure that the module is available to the client package's Vite config._
|
|
587
601
|
|
|
588
|
-
During development, the plugin watches the server module and the files it imports. When one of those files changes, fate
|
|
602
|
+
During development, the plugin watches the server module and the files it imports. When one of those files changes, fate updates the internal client wiring and invalidates `@nkzw/fate/client` in Vite's module graph.
|
|
589
603
|
|
|
590
|
-
For a barebones client without React, import the plugin from `@nkzw/fate/vite` and the
|
|
604
|
+
For a barebones client without React, import the plugin from `@nkzw/fate/vite` and the client APIs from `@nkzw/fate/client`. The plugin wires the same server types for the selected import path.
|
|
591
605
|
|
|
592
606
|
The plugin writes project-local types under `.fate/`. If your TypeScript config does not already include dot-directories, extend the generated config:
|
|
593
607
|
|
|
@@ -599,7 +613,7 @@ The plugin writes project-local types under `.fate/`. If your TypeScript config
|
|
|
599
613
|
|
|
600
614
|
## Creating a _fate_ Client
|
|
601
615
|
|
|
602
|
-
Now that the Vite plugin
|
|
616
|
+
Now that the Vite plugin has connected the types, create a fate client instance and provide it to your React app with the `FateClient` context provider:
|
|
603
617
|
|
|
604
618
|
```tsx
|
|
605
619
|
import { httpBatchLink } from '@trpc/client';
|
package/docs/guide/views.md
CHANGED
|
@@ -104,7 +104,7 @@ This code fetches the author associated with the Post and makes it available to
|
|
|
104
104
|
In fate, views are composable and reusable. Instead of inlining the selection, we can define a `UserView` and compose it into the `PostView` like this:
|
|
105
105
|
|
|
106
106
|
```tsx
|
|
107
|
-
import type { Post, User } from '@your-org/server/
|
|
107
|
+
import type { Post, User } from '@your-org/server/views';
|
|
108
108
|
import { view } from 'react-fate';
|
|
109
109
|
|
|
110
110
|
export const UserView = view<User>()({
|
|
@@ -33,7 +33,7 @@ export default defineConfig({
|
|
|
33
33
|
});
|
|
34
34
|
```
|
|
35
35
|
|
|
36
|
-
The
|
|
36
|
+
The Void transport uses `/fate` for RPC requests and `/fate-live` for live
|
|
37
37
|
updates by default. In SSR, it calls the exported fate server directly. In the
|
|
38
38
|
browser, it uses fetch and the SSE live endpoint.
|
|
39
39
|
|
|
@@ -106,8 +106,8 @@ control messages. `void-fate` does not use WebSockets.
|
|
|
106
106
|
|
|
107
107
|
## React Layout
|
|
108
108
|
|
|
109
|
-
Wrap your app with `VoidFateClient` from `void-fate/react`. It creates
|
|
110
|
-
|
|
109
|
+
Wrap your app with `VoidFateClient` from `void-fate/react`. It creates and
|
|
110
|
+
provides the fate client through `react-fate`:
|
|
111
111
|
|
|
112
112
|
```tsx
|
|
113
113
|
import { useShared } from '@void/react';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-fate",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "fate is a modern data client for React.",
|
|
5
5
|
"homepage": "https://github.com/nkzw-tech/fate",
|
|
6
6
|
"license": "MIT",
|
|
@@ -40,13 +40,13 @@
|
|
|
40
40
|
}
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"@nkzw/fate": "^1.0.
|
|
43
|
+
"@nkzw/fate": "^1.0.1"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
46
|
"@types/react": "^19.2.14",
|
|
47
47
|
"@types/react-dom": "^19.2.3",
|
|
48
|
-
"react": "^19.2.
|
|
49
|
-
"react-dom": "^19.2.
|
|
48
|
+
"react": "^19.2.6",
|
|
49
|
+
"react-dom": "^19.2.6"
|
|
50
50
|
},
|
|
51
51
|
"peerDependencies": {
|
|
52
52
|
"react": "^19.2.0",
|