react-fate 0.1.3 → 1.0.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/README.md +810 -129
- package/docs/api/functions/FateClient.md +23 -0
- package/docs/api/functions/clientRoot.md +27 -0
- package/docs/api/functions/createClient.md +21 -0
- package/docs/api/functions/createHTTPTransport.md +51 -0
- package/docs/api/functions/createTRPCTransport.md +46 -0
- package/docs/api/functions/mutation.md +32 -0
- package/docs/api/functions/useFateClient.md +17 -0
- package/docs/api/functions/useListView.md +28 -0
- package/docs/api/functions/useLiveListView.md +28 -0
- package/docs/api/functions/useLiveView.md +38 -0
- package/docs/api/functions/useRequest.md +38 -0
- package/docs/api/functions/useView.md +37 -0
- package/docs/api/functions/view.md +26 -0
- package/docs/api/index.md +35 -0
- package/docs/api/type-aliases/ConnectionRef.md +13 -0
- package/docs/api/type-aliases/InferFateAPI.md +11 -0
- package/docs/api/type-aliases/ViewRef.md +13 -0
- package/docs/api/variables/toEntityId.md +21 -0
- package/docs/guide/actions.md +263 -0
- package/docs/guide/core-concepts.md +22 -0
- package/docs/guide/getting-started.md +57 -0
- package/docs/guide/list-views.md +86 -0
- package/docs/guide/live-views.md +245 -0
- package/docs/guide/requests.md +130 -0
- package/docs/guide/server-integration.md +630 -0
- package/docs/guide/views.md +278 -0
- package/docs/guide/void-integration.md +167 -0
- package/docs/index.md +4 -0
- package/lib/cli.d.mts +1 -1
- package/lib/cli.mjs +1 -2
- package/lib/client.d.mts +7 -0
- package/lib/client.mjs +2 -0
- package/lib/index.d.mts +41 -17
- package/lib/index.mjs +96 -46
- package/lib/vite.d.mts +12 -0
- package/lib/vite.mjs +8 -0
- package/package.json +17 -6
package/lib/index.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { ConnectionTag, clientRoot, createClient, createTRPCTransport, isViewTag, mutation, toEntityId, view } from "@nkzw/fate";
|
|
2
|
-
import { createContext, use, useCallback, useDeferredValue, useEffect, useMemo, useRef, useSyncExternalStore } from "react";
|
|
1
|
+
import { ConnectionTag, clientRoot, createClient, createHTTPTransport, createTRPCTransport, isViewTag, mutation, toEntityId, view } from "@nkzw/fate";
|
|
2
|
+
import { createContext, use, useCallback, useDeferredValue, useEffect, useEffectEvent, useMemo, useRef, useSyncExternalStore } from "react";
|
|
3
3
|
import { jsx } from "react/jsx-runtime";
|
|
4
|
-
|
|
4
|
+
import { getListEntries } from "@nkzw/fate/list";
|
|
5
5
|
//#region src/context.tsx
|
|
6
6
|
const FateContext = createContext(null);
|
|
7
7
|
/**
|
|
@@ -21,7 +21,6 @@ function useFateClient() {
|
|
|
21
21
|
if (!context) throw new Error(`react-fate: '<FateClient client={fate}>' is missing.`);
|
|
22
22
|
return context;
|
|
23
23
|
}
|
|
24
|
-
|
|
25
24
|
//#endregion
|
|
26
25
|
//#region src/useView.tsx
|
|
27
26
|
const nullSnapshot = {
|
|
@@ -31,7 +30,7 @@ const nullSnapshot = {
|
|
|
31
30
|
},
|
|
32
31
|
value: null
|
|
33
32
|
};
|
|
34
|
-
function useView(view
|
|
33
|
+
function useView(view, ref) {
|
|
35
34
|
const client = useFateClient();
|
|
36
35
|
const snapshotRef = useRef(null);
|
|
37
36
|
const getSnapshot = useCallback(() => {
|
|
@@ -39,12 +38,12 @@ function useView(view$1, ref) {
|
|
|
39
38
|
snapshotRef.current = null;
|
|
40
39
|
return nullSnapshot;
|
|
41
40
|
}
|
|
42
|
-
const snapshot
|
|
43
|
-
snapshotRef.current = snapshot
|
|
44
|
-
return snapshot
|
|
41
|
+
const snapshot = client.readView(view, ref);
|
|
42
|
+
snapshotRef.current = snapshot.status === "fulfilled" ? snapshot.value : null;
|
|
43
|
+
return snapshot;
|
|
45
44
|
}, [
|
|
46
45
|
client,
|
|
47
|
-
view
|
|
46
|
+
view,
|
|
48
47
|
ref
|
|
49
48
|
]);
|
|
50
49
|
const snapshot = use(useDeferredValue(useSyncExternalStore(useCallback((onStoreChange) => {
|
|
@@ -80,49 +79,48 @@ function useView(view$1, ref) {
|
|
|
80
79
|
}, [client.store, ref]), getSnapshot, getSnapshot)));
|
|
81
80
|
return snapshot ? snapshot.data : null;
|
|
82
81
|
}
|
|
83
|
-
|
|
84
82
|
//#endregion
|
|
85
|
-
//#region src/
|
|
86
|
-
|
|
87
|
-
* Declares the data a screen needs and kicks off fetching, suspending while the
|
|
88
|
-
* request resolves.
|
|
89
|
-
*
|
|
90
|
-
* @example
|
|
91
|
-
* const { posts } = useRequest({ posts: { list: PostView } });
|
|
92
|
-
*/
|
|
93
|
-
function useRequest(request, options) {
|
|
83
|
+
//#region src/useLiveView.tsx
|
|
84
|
+
function useLiveView(view, ref) {
|
|
94
85
|
const client = useFateClient();
|
|
95
|
-
const
|
|
96
|
-
const
|
|
97
|
-
|
|
98
|
-
if (
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
}
|
|
86
|
+
const liveId = ref?.id;
|
|
87
|
+
const liveType = ref?.__typename;
|
|
88
|
+
const subscribeLiveView = useEffectEvent(() => {
|
|
89
|
+
if (ref === null) return;
|
|
90
|
+
client.assertLiveViewSupport();
|
|
91
|
+
return client.subscribeLiveView(view, ref);
|
|
92
|
+
});
|
|
93
|
+
useEffect(() => subscribeLiveView(), [
|
|
102
94
|
client,
|
|
103
|
-
|
|
104
|
-
|
|
95
|
+
view,
|
|
96
|
+
liveId,
|
|
97
|
+
liveType
|
|
105
98
|
]);
|
|
106
|
-
return
|
|
99
|
+
return useView(view, ref);
|
|
107
100
|
}
|
|
108
|
-
|
|
109
101
|
//#endregion
|
|
110
|
-
//#region src/
|
|
111
|
-
const getNodeView = (view
|
|
112
|
-
const maybeView = view
|
|
113
|
-
if (maybeView) {
|
|
102
|
+
//#region src/listView.ts
|
|
103
|
+
const getNodeView = (view) => {
|
|
104
|
+
const maybeView = view.items?.node;
|
|
105
|
+
if (maybeView && typeof maybeView === "object") {
|
|
114
106
|
for (const key of Object.keys(maybeView)) if (isViewTag(key)) return maybeView;
|
|
115
107
|
}
|
|
116
|
-
return view
|
|
108
|
+
return view;
|
|
117
109
|
};
|
|
110
|
+
const getConnectionMetadata = (connection) => connection && typeof connection === "object" ? connection[ConnectionTag] : null;
|
|
111
|
+
const useListViewInfo = (selection, connection) => ({
|
|
112
|
+
metadata: getConnectionMetadata(connection),
|
|
113
|
+
nodeView: useMemo(() => getNodeView(selection), [selection])
|
|
114
|
+
});
|
|
115
|
+
//#endregion
|
|
116
|
+
//#region src/useListView.tsx
|
|
118
117
|
/**
|
|
119
118
|
* Subscribes to a connection field, returning the current items and pagination
|
|
120
119
|
* helpers to load the next or previous page.
|
|
121
120
|
*/
|
|
122
121
|
function useListView(selection, connection) {
|
|
123
122
|
const client = useFateClient();
|
|
124
|
-
const nodeView =
|
|
125
|
-
const metadata = connection && typeof connection === "object" ? connection[ConnectionTag] : null;
|
|
123
|
+
const { metadata, nodeView } = useListViewInfo(selection, connection);
|
|
126
124
|
const subscribe = useCallback((onStoreChange) => metadata ? client.store.subscribeList(metadata.key, onStoreChange) : () => {}, [client, metadata]);
|
|
127
125
|
const getSnapshot = useCallback(() => metadata ? client.store.getListState(metadata.key) : void 0, [client, metadata]);
|
|
128
126
|
const listState = useDeferredValue(useSyncExternalStore(subscribe, getSnapshot, getSnapshot));
|
|
@@ -133,8 +131,8 @@ function useListView(selection, connection) {
|
|
|
133
131
|
const previousCursor = pagination?.previousCursor;
|
|
134
132
|
return [
|
|
135
133
|
useMemo(() => {
|
|
136
|
-
if (metadata
|
|
137
|
-
cursor
|
|
134
|
+
if (metadata && listState) return getListEntries(listState).map(({ cursor, id }) => ({
|
|
135
|
+
cursor,
|
|
138
136
|
node: client.rootListRef(id, nodeView)
|
|
139
137
|
}));
|
|
140
138
|
return connection?.items;
|
|
@@ -142,18 +140,20 @@ function useListView(selection, connection) {
|
|
|
142
140
|
client,
|
|
143
141
|
connection?.items,
|
|
144
142
|
listState,
|
|
145
|
-
metadata
|
|
143
|
+
metadata,
|
|
146
144
|
nodeView
|
|
147
145
|
]),
|
|
148
146
|
useMemo(() => {
|
|
149
147
|
if (!metadata || !hasNext || !nextCursor) return null;
|
|
150
148
|
return async () => {
|
|
151
|
-
const { before, first, last, ...values } = metadata.args || {};
|
|
149
|
+
const { before: _before, first, last, ...values } = metadata.args || {};
|
|
152
150
|
const nextPageSize = first ?? last;
|
|
153
151
|
await client.loadConnection(nodeView, metadata, {
|
|
154
152
|
...values,
|
|
155
153
|
after: nextCursor,
|
|
156
|
-
|
|
154
|
+
before: void 0,
|
|
155
|
+
...nextPageSize !== void 0 ? { first: nextPageSize } : null,
|
|
156
|
+
last: void 0
|
|
157
157
|
}, { direction: "forward" });
|
|
158
158
|
};
|
|
159
159
|
}, [
|
|
@@ -166,10 +166,14 @@ function useListView(selection, connection) {
|
|
|
166
166
|
useMemo(() => {
|
|
167
167
|
if (!metadata || !hasPrevious || !previousCursor) return null;
|
|
168
168
|
return async () => {
|
|
169
|
-
const { after, ...values } = metadata.args || {};
|
|
169
|
+
const { after: _after, first, last, ...values } = metadata.args || {};
|
|
170
|
+
const previousPageSize = last ?? first;
|
|
170
171
|
await client.loadConnection(nodeView, metadata, {
|
|
171
172
|
...values,
|
|
172
|
-
|
|
173
|
+
after: void 0,
|
|
174
|
+
before: previousCursor,
|
|
175
|
+
first: void 0,
|
|
176
|
+
...previousPageSize !== void 0 ? { last: previousPageSize } : null
|
|
173
177
|
}, { direction: "backward" });
|
|
174
178
|
};
|
|
175
179
|
}, [
|
|
@@ -181,6 +185,52 @@ function useListView(selection, connection) {
|
|
|
181
185
|
])
|
|
182
186
|
];
|
|
183
187
|
}
|
|
184
|
-
|
|
185
188
|
//#endregion
|
|
186
|
-
|
|
189
|
+
//#region src/useLiveListView.tsx
|
|
190
|
+
/**
|
|
191
|
+
* Subscribes to a connection field, returning live-updating items and pagination
|
|
192
|
+
* helpers to load the next or previous page.
|
|
193
|
+
*/
|
|
194
|
+
function useLiveListView(selection, connection) {
|
|
195
|
+
const client = useFateClient();
|
|
196
|
+
const { metadata, nodeView } = useListViewInfo(selection, connection);
|
|
197
|
+
const subscribeLiveListView = useEffectEvent(() => {
|
|
198
|
+
if (!metadata) return;
|
|
199
|
+
client.assertLiveConnectionSupport();
|
|
200
|
+
return client.subscribeLiveListView(nodeView, metadata);
|
|
201
|
+
});
|
|
202
|
+
useEffect(() => subscribeLiveListView(), [
|
|
203
|
+
client,
|
|
204
|
+
metadata?.key,
|
|
205
|
+
nodeView
|
|
206
|
+
]);
|
|
207
|
+
return useListView(selection, connection);
|
|
208
|
+
}
|
|
209
|
+
//#endregion
|
|
210
|
+
//#region src/useRequest.tsx
|
|
211
|
+
/**
|
|
212
|
+
* Declares the data a screen needs and kicks off fetching, suspending while the
|
|
213
|
+
* request resolves.
|
|
214
|
+
*
|
|
215
|
+
* @example
|
|
216
|
+
* const { posts } = useRequest({ posts: { list: PostView } });
|
|
217
|
+
*/
|
|
218
|
+
function useRequest(request, options) {
|
|
219
|
+
const client = useFateClient();
|
|
220
|
+
const mode = options?.mode ?? "cache-first";
|
|
221
|
+
const { promise, requestKey } = client.prepareRequestForRender(request, options);
|
|
222
|
+
useEffect(() => {
|
|
223
|
+
const retained = client.retainRequestKey(requestKey, mode);
|
|
224
|
+
return () => {
|
|
225
|
+
retained.dispose();
|
|
226
|
+
if (mode === "network-only" || mode === "stale-while-revalidate") client.releaseRequestKey(requestKey, mode);
|
|
227
|
+
};
|
|
228
|
+
}, [
|
|
229
|
+
client,
|
|
230
|
+
mode,
|
|
231
|
+
requestKey
|
|
232
|
+
]);
|
|
233
|
+
return use(useDeferredValue(promise));
|
|
234
|
+
}
|
|
235
|
+
//#endregion
|
|
236
|
+
export { FateClient, clientRoot, createClient, createHTTPTransport, createTRPCTransport, mutation, toEntityId, useFateClient, useListView, useLiveListView, useLiveView, useRequest, useView, view };
|
package/lib/vite.d.mts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { FateViteTransport, fate as fate$1 } from "@nkzw/fate/vite";
|
|
2
|
+
|
|
3
|
+
//#region src/vite.d.ts
|
|
4
|
+
type FateVitePluginOptions = {
|
|
5
|
+
generatedFile?: false | string;
|
|
6
|
+
module: string;
|
|
7
|
+
transport?: FateViteTransport;
|
|
8
|
+
tsconfigFile?: false | string;
|
|
9
|
+
};
|
|
10
|
+
declare const fate: (options: FateVitePluginOptions) => ReturnType<typeof fate$1>;
|
|
11
|
+
//#endregion
|
|
12
|
+
export { FateVitePluginOptions, fate };
|
package/lib/vite.mjs
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-fate",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "fate is a modern data client for React.",
|
|
5
5
|
"homepage": "https://github.com/nkzw-tech/fate",
|
|
6
6
|
"license": "MIT",
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
"fate": "./lib/cli.mjs"
|
|
17
17
|
},
|
|
18
18
|
"files": [
|
|
19
|
+
"docs",
|
|
19
20
|
"lib"
|
|
20
21
|
],
|
|
21
22
|
"type": "module",
|
|
@@ -26,22 +27,32 @@
|
|
|
26
27
|
"types": "./lib/index.d.mts",
|
|
27
28
|
"@nkzw/source": "./src/index.tsx",
|
|
28
29
|
"default": "./lib/index.mjs"
|
|
30
|
+
},
|
|
31
|
+
"./client": {
|
|
32
|
+
"types": "./lib/client.d.mts",
|
|
33
|
+
"@nkzw/source": "./src/client.ts",
|
|
34
|
+
"default": "./lib/client.mjs"
|
|
35
|
+
},
|
|
36
|
+
"./vite": {
|
|
37
|
+
"types": "./lib/vite.d.mts",
|
|
38
|
+
"@nkzw/source": "./src/vite.ts",
|
|
39
|
+
"default": "./lib/vite.mjs"
|
|
29
40
|
}
|
|
30
41
|
},
|
|
31
42
|
"dependencies": {
|
|
32
|
-
"@nkzw/fate": "^0.
|
|
43
|
+
"@nkzw/fate": "^1.0.0"
|
|
33
44
|
},
|
|
34
45
|
"devDependencies": {
|
|
35
|
-
"@types/react": "^19.2.
|
|
46
|
+
"@types/react": "^19.2.14",
|
|
36
47
|
"@types/react-dom": "^19.2.3",
|
|
37
|
-
"react": "^19.2.
|
|
38
|
-
"react-dom": "^19.2.
|
|
48
|
+
"react": "^19.2.6",
|
|
49
|
+
"react-dom": "^19.2.6"
|
|
39
50
|
},
|
|
40
51
|
"peerDependencies": {
|
|
41
52
|
"react": "^19.2.0",
|
|
42
53
|
"react-dom": "^19.2.0"
|
|
43
54
|
},
|
|
44
55
|
"scripts": {
|
|
45
|
-
"build": "
|
|
56
|
+
"build": "vp pack -d lib --target=node24 src/index.tsx src/client.ts src/cli.ts src/vite.ts"
|
|
46
57
|
}
|
|
47
58
|
}
|