react-fate 1.3.0 → 1.3.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 CHANGED
@@ -2676,7 +2676,23 @@ _And you are all set. Happy building!_
2676
2676
  Use this integration when your app runs on Void and you want the example app's
2677
2677
  setup without copying its adapter glue.
2678
2678
 
2679
- ### Install
2679
+ ### New Project
2680
+
2681
+ For a new Void app, start from the Void template. It includes the client, Void routes, Drizzle setup, live transport, auth wiring, and generated fate client setup.
2682
+
2683
+ ```sh
2684
+ vp create fate my-app --template void
2685
+ ```
2686
+
2687
+ Use Vue instead of React with:
2688
+
2689
+ ```sh
2690
+ vp create fate my-app --template void --framework vue
2691
+ ```
2692
+
2693
+ ### Existing Project
2694
+
2695
+ For an existing Void project, add the packages directly:
2680
2696
 
2681
2697
  ::: code-group
2682
2698
 
@@ -2919,6 +2935,168 @@ falls back to the in-memory live bus for the current request context.
2919
2935
  The live transport is best-effort and does not replay missed events after a
2920
2936
  client reconnects. This matches fate's default in-memory live event bus.
2921
2937
 
2938
+ ## Cloudflare Integration
2939
+
2940
+ `cf-fate` is the first-class Cloudflare Workers adapter for Fate native HTTP transport and live views.
2941
+
2942
+ Use it when your backend runs directly on Cloudflare Workers and you want fate live views without adopting the Void platform.
2943
+
2944
+ ### New Project
2945
+
2946
+ For a new Cloudflare Workers app, start from the Cloudflare template. It includes the client, Worker server, D1 migrations, Wrangler config, Durable Object live transport, auth wiring, and generated fate client setup.
2947
+
2948
+ ```sh
2949
+ vp create fate my-app --template cloudflare
2950
+ ```
2951
+
2952
+ Use Vue instead of React with:
2953
+
2954
+ ```sh
2955
+ vp create fate my-app --template cloudflare --framework vue
2956
+ ```
2957
+
2958
+ ### Existing Project
2959
+
2960
+ For an existing Cloudflare Workers project, add the packages directly:
2961
+
2962
+ ```sh
2963
+ pnpm add @nkzw/fate react-fate cf-fate drizzle-orm
2964
+ pnpm add -D wrangler
2965
+ ```
2966
+
2967
+ For Vue clients, replace `react-fate` with `vue-fate`.
2968
+
2969
+ ### Server Setup
2970
+
2971
+ Create a Cloudflare live stream and pass its Fate live facade to `createFateServer`.
2972
+
2973
+ ```ts
2974
+ // src/fate/live.ts
2975
+ import { defineCloudflareFateLiveStream } from 'cf-fate/server';
2976
+
2977
+ export const fateStream = defineCloudflareFateLiveStream({
2978
+ allowAnonymousControl: true,
2979
+ binding: 'FATE_LIVE',
2980
+ id: 'fate',
2981
+ });
2982
+ ```
2983
+
2984
+ ```ts
2985
+ // src/fate/server.ts
2986
+ import { createFateServer } from '@nkzw/fate/server';
2987
+ import { createCloudflareFateLive } from 'cf-fate/server';
2988
+
2989
+ export const fateLive = createCloudflareFateLive();
2990
+ export const { live } = fateLive;
2991
+
2992
+ export const fateServer = createFateServer({
2993
+ live,
2994
+ // context,
2995
+ // roots,
2996
+ // sources,
2997
+ });
2998
+ ```
2999
+
3000
+ Publish from mutations through the normal Fate live bus:
3001
+
3002
+ ```ts
3003
+ live.update('Post', postId, { changed: ['likes'] });
3004
+ live.connection('Post.comments', { id: postId }).appendNode('Comment', commentId);
3005
+ ```
3006
+
3007
+ ### Worker Routes
3008
+
3009
+ Expose one route for Fate RPC and one route for the SSE live stream.
3010
+
3011
+ ```ts
3012
+ import {
3013
+ createCloudflareFateLiveDurableObject,
3014
+ defineCloudflareFateLiveRoute,
3015
+ defineCloudflareFateRoute,
3016
+ } from 'cf-fate/server';
3017
+ import { fateStream } from './fate/live';
3018
+ import { fateLive, fateServer } from './fate/server';
3019
+
3020
+ const fateRoute = defineCloudflareFateRoute(fateServer, fateLive, { stream: fateStream });
3021
+ const fateLiveRoute = defineCloudflareFateLiveRoute(fateStream);
3022
+
3023
+ export const FateLiveDurableObject = createCloudflareFateLiveDurableObject({
3024
+ binding: 'FATE_LIVE',
3025
+ });
3026
+
3027
+ export default {
3028
+ fetch(request, env, ctx) {
3029
+ const url = new URL(request.url);
3030
+ if (url.pathname === '/fate') {
3031
+ return fateRoute.fetch(request, env, ctx);
3032
+ }
3033
+ if (url.pathname === '/fate-live') {
3034
+ return fateLiveRoute.fetch(request, env, ctx);
3035
+ }
3036
+ return new Response('Not Found', { status: 404 });
3037
+ },
3038
+ };
3039
+ ```
3040
+
3041
+ ### Wrangler
3042
+
3043
+ Add a Durable Object binding and migration. `cf-fate` uses `node:async_hooks`, so the Worker must enable Node compatibility.
3044
+
3045
+ ```jsonc
3046
+ {
3047
+ "compatibility_flags": ["nodejs_compat"],
3048
+ "durable_objects": {
3049
+ "bindings": [
3050
+ {
3051
+ "name": "FATE_LIVE",
3052
+ "class_name": "FateLiveDurableObject",
3053
+ },
3054
+ ],
3055
+ },
3056
+ "migrations": [
3057
+ {
3058
+ "tag": "fate-live-v1",
3059
+ "new_sqlite_classes": ["FateLiveDurableObject"],
3060
+ },
3061
+ ],
3062
+ }
3063
+ ```
3064
+
3065
+ ### Client
3066
+
3067
+ Use the Cloudflare transport in the Fate Vite plugin:
3068
+
3069
+ ```ts
3070
+ import { fate } from 'react-fate/vite';
3071
+
3072
+ fate({
3073
+ module: './src/fate/server.ts',
3074
+ transport: 'cloudflare',
3075
+ });
3076
+ ```
3077
+
3078
+ Then point the generated client at the Worker endpoints:
3079
+
3080
+ ```tsx
3081
+ import { FateClient } from 'react-fate';
3082
+ import { createFateClient } from 'react-fate/client';
3083
+
3084
+ const fate = createFateClient({
3085
+ liveUrl: 'http://localhost:8787/fate-live',
3086
+ url: 'http://localhost:8787/fate',
3087
+ });
3088
+
3089
+ export function App({ children }) {
3090
+ return <FateClient client={fate}>{children}</FateClient>;
3091
+ }
3092
+ ```
3093
+
3094
+ ### Semantics
3095
+
3096
+ `cf-fate` uses one browser `EventSource` per Fate client and multiplexes entity and connection topics over that stream. Durable Objects keep connection and topic subscription state so later requests, mutations, scheduled handlers, and queue consumers can publish to already-connected clients.
3097
+
3098
+ Delivery is at-most-once. Events are ordered within one topic, but events are not durably replayed after a disconnect. Use authoritative refetching or application-owned replay storage if missed events must be recovered.
3099
+
2922
3100
  ## Frequently Asked Questions
2923
3101
 
2924
3102
  ### Is this serious software?
@@ -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/4876b00b02cd1ef9ae7fd82b8a3c27d82fc6c232/packages/react-fate/src/context.tsx#L17)
5
+ Defined in: [packages/react-fate/src/context.tsx:17](https://github.com/nkzw-tech/fate/blob/bdff68417591ff0372c03c214bbc7c722cdf13a2/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/4876b00b02cd1ef9ae7fd82b8a3c27d82fc6c232/packages/react-fate/src/context.tsx#L30)
5
+ Defined in: [packages/react-fate/src/context.tsx:30](https://github.com/nkzw-tech/fate/blob/bdff68417591ff0372c03c214bbc7c722cdf13a2/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`\<`ResolvedConnection`\<`C`\>\>, `LoadMoreFn` \| `null`, `LoadMoreFn` \| `null`\]
4
4
 
5
- Defined in: [packages/react-fate/src/useListView.tsx:19](https://github.com/nkzw-tech/fate/blob/4876b00b02cd1ef9ae7fd82b8a3c27d82fc6c232/packages/react-fate/src/useListView.tsx#L19)
5
+ Defined in: [packages/react-fate/src/useListView.tsx:19](https://github.com/nkzw-tech/fate/blob/bdff68417591ff0372c03c214bbc7c722cdf13a2/packages/react-fate/src/useListView.tsx#L19)
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`\<`ResolvedConnection`\<`C`\>\>, `LoadMoreFn` \| `null`, `LoadMoreFn` \| `null`\]
4
4
 
5
- Defined in: [packages/react-fate/src/useLiveListView.tsx:19](https://github.com/nkzw-tech/fate/blob/4876b00b02cd1ef9ae7fd82b8a3c27d82fc6c232/packages/react-fate/src/useLiveListView.tsx#L19)
5
+ Defined in: [packages/react-fate/src/useLiveListView.tsx:19](https://github.com/nkzw-tech/fate/blob/bdff68417591ff0372c03c214bbc7c722cdf13a2/packages/react-fate/src/useLiveListView.tsx#L19)
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.
@@ -4,7 +4,7 @@
4
4
 
5
5
  > **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\]?: DeferrableSelectionFieldValue\<ViewEntityWithTypename\<V\>, K\> \} & `object` & `SelectionViewSpread`\<`ViewEntityWithTypename`\<`V`\>\> & `ViewSelection`\<`V`\>\> : `ViewEntityWithTypename`\<`V`\> & `object`\>
6
6
 
7
- Defined in: [packages/react-fate/src/useLiveView.tsx:27](https://github.com/nkzw-tech/fate/blob/4876b00b02cd1ef9ae7fd82b8a3c27d82fc6c232/packages/react-fate/src/useLiveView.tsx#L27)
7
+ Defined in: [packages/react-fate/src/useLiveView.tsx:27](https://github.com/nkzw-tech/fate/blob/bdff68417591ff0372c03c214bbc7c722cdf13a2/packages/react-fate/src/useLiveView.tsx#L27)
8
8
 
9
9
  Resolves a reference against a view and subscribes to live server updates for
10
10
  that selection.
@@ -43,7 +43,7 @@ const post = useLiveView(PostView, postRef);
43
43
 
44
44
  > **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\]?: DeferrableSelectionFieldValue\<ViewEntityWithTypename\<V\>, K\> \} & `object` & `SelectionViewSpread`\<`ViewEntityWithTypename`\<`V`\>\> & `ViewSelection`\<`V`\>\> : `ViewEntityWithTypename`\<`V`\> & `object`\>
45
45
 
46
- Defined in: [packages/react-fate/src/useLiveView.tsx:31](https://github.com/nkzw-tech/fate/blob/4876b00b02cd1ef9ae7fd82b8a3c27d82fc6c232/packages/react-fate/src/useLiveView.tsx#L31)
46
+ Defined in: [packages/react-fate/src/useLiveView.tsx:31](https://github.com/nkzw-tech/fate/blob/bdff68417591ff0372c03c214bbc7c722cdf13a2/packages/react-fate/src/useLiveView.tsx#L31)
47
47
 
48
48
  Resolves a reference against a view and subscribes to live server updates for
49
49
  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/4876b00b02cd1ef9ae7fd82b8a3c27d82fc6c232/packages/react-fate/src/useRequest.tsx#L25)
5
+ Defined in: [packages/react-fate/src/useRequest.tsx:25](https://github.com/nkzw-tech/fate/blob/bdff68417591ff0372c03c214bbc7c722cdf13a2/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.
@@ -4,7 +4,7 @@
4
4
 
5
5
  > **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\]?: DeferrableSelectionFieldValue\<ViewEntityWithTypename\<V\>, K\> \} & `object` & `SelectionViewSpread`\<`ViewEntityWithTypename`\<`V`\>\> & `ViewSelection`\<`V`\>\> : `ViewEntityWithTypename`\<`V`\> & `object`\>
6
6
 
7
- Defined in: [packages/react-fate/src/useView.tsx:40](https://github.com/nkzw-tech/fate/blob/4876b00b02cd1ef9ae7fd82b8a3c27d82fc6c232/packages/react-fate/src/useView.tsx#L40)
7
+ Defined in: [packages/react-fate/src/useView.tsx:40](https://github.com/nkzw-tech/fate/blob/bdff68417591ff0372c03c214bbc7c722cdf13a2/packages/react-fate/src/useView.tsx#L40)
8
8
 
9
9
  Resolves a reference against a view and subscribes to updates for that selection.
10
10
 
@@ -42,7 +42,7 @@ const post = useView(PostView, postRef);
42
42
 
43
43
  > **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\]?: DeferrableSelectionFieldValue\<ViewEntityWithTypename\<V\>, K\> \} & `object` & `SelectionViewSpread`\<`ViewEntityWithTypename`\<`V`\>\> & `ViewSelection`\<`V`\>\> : `ViewEntityWithTypename`\<`V`\> & `object`\>
44
44
 
45
- Defined in: [packages/react-fate/src/useView.tsx:44](https://github.com/nkzw-tech/fate/blob/4876b00b02cd1ef9ae7fd82b8a3c27d82fc6c232/packages/react-fate/src/useView.tsx#L44)
45
+ Defined in: [packages/react-fate/src/useView.tsx:44](https://github.com/nkzw-tech/fate/blob/bdff68417591ff0372c03c214bbc7c722cdf13a2/packages/react-fate/src/useView.tsx#L44)
46
46
 
47
47
  Resolves a reference against a view and subscribes to updates for that selection.
48
48
 
@@ -80,7 +80,7 @@ const post = useView(PostView, postRef);
80
80
 
81
81
  > **useView**\<`V`\>(`view`, `ref`): `Readonly`\<`ViewSelection`\<`V`\> *extends* `Selection`\<`ViewEntityWithTypename`\<`V`\>\> ? `Mask`\<`ViewEntityWithTypename`\<`V`\>, \{ \[K in string \| number \| symbol as K extends "\_\_typename" ? never : K\]?: DeferrableSelectionFieldValue\<ViewEntityWithTypename\<V\>, K\> \} & `object` & `SelectionViewSpread`\<`ViewEntityWithTypename`\<`V`\>\> & `ViewSelection`\<`V`\>\> : `ViewEntityWithTypename`\<`V`\> & `object`\> \| `null`
82
82
 
83
- Defined in: [packages/react-fate/src/useView.tsx:48](https://github.com/nkzw-tech/fate/blob/4876b00b02cd1ef9ae7fd82b8a3c27d82fc6c232/packages/react-fate/src/useView.tsx#L48)
83
+ Defined in: [packages/react-fate/src/useView.tsx:48](https://github.com/nkzw-tech/fate/blob/bdff68417591ff0372c03c214bbc7c722cdf13a2/packages/react-fate/src/useView.tsx#L48)
84
84
 
85
85
  Resolves a reference against a view and subscribes to updates for that selection.
86
86
 
@@ -4,13 +4,31 @@
4
4
 
5
5
  Use it when your backend runs directly on Cloudflare Workers and you want fate live views without adopting the Void platform.
6
6
 
7
- ## Install
7
+ ## New Project
8
+
9
+ For a new Cloudflare Workers app, start from the Cloudflare template. It includes the client, Worker server, D1 migrations, Wrangler config, Durable Object live transport, auth wiring, and generated fate client setup.
10
+
11
+ ```sh
12
+ vp create fate my-app --template cloudflare
13
+ ```
14
+
15
+ Use Vue instead of React with:
16
+
17
+ ```sh
18
+ vp create fate my-app --template cloudflare --framework vue
19
+ ```
20
+
21
+ ## Existing Project
22
+
23
+ For an existing Cloudflare Workers project, add the packages directly:
8
24
 
9
25
  ```sh
10
26
  pnpm add @nkzw/fate react-fate cf-fate drizzle-orm
11
27
  pnpm add -D wrangler
12
28
  ```
13
29
 
30
+ For Vue clients, replace `react-fate` with `vue-fate`.
31
+
14
32
  ## Server Setup
15
33
 
16
34
  Create a Cloudflare live stream and pass its Fate live facade to `createFateServer`.
@@ -5,7 +5,23 @@
5
5
  Use this integration when your app runs on Void and you want the example app's
6
6
  setup without copying its adapter glue.
7
7
 
8
- ## Install
8
+ ## New Project
9
+
10
+ For a new Void app, start from the Void template. It includes the client, Void routes, Drizzle setup, live transport, auth wiring, and generated fate client setup.
11
+
12
+ ```sh
13
+ vp create fate my-app --template void
14
+ ```
15
+
16
+ Use Vue instead of React with:
17
+
18
+ ```sh
19
+ vp create fate my-app --template void --framework vue
20
+ ```
21
+
22
+ ## Existing Project
23
+
24
+ For an existing Void project, add the packages directly:
9
25
 
10
26
  ::: code-group
11
27
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-fate",
3
- "version": "1.3.0",
3
+ "version": "1.3.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,7 +40,7 @@
40
40
  }
41
41
  },
42
42
  "dependencies": {
43
- "@nkzw/fate": "^1.3.0"
43
+ "@nkzw/fate": "^1.3.1"
44
44
  },
45
45
  "devDependencies": {
46
46
  "@types/react": "^19.2.16",