@nkzw/fate 1.1.0 → 1.2.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 +745 -24
- package/docs/api/classes/FateClient.md +63 -39
- package/docs/api/functions/clientRoot.md +1 -1
- package/docs/api/functions/createClient.md +1 -1
- package/docs/api/functions/createGraphQLTransport.md +1 -1
- package/docs/api/functions/createHTTPTransport.md +1 -1
- package/docs/api/functions/createTRPCTransport.md +1 -1
- package/docs/api/functions/defer.md +21 -0
- package/docs/api/functions/getDeferredMetadata.md +21 -0
- package/docs/api/functions/getListEntries.md +1 -1
- package/docs/api/functions/getSelectionPlan.md +1 -1
- package/docs/api/functions/graphqlMutation.md +1 -1
- package/docs/api/functions/isDeferred.md +15 -0
- package/docs/api/functions/isRecord.md +1 -1
- package/docs/api/functions/isViewTag.md +1 -1
- package/docs/api/functions/liveConnectionTopic.md +1 -1
- package/docs/api/functions/liveEntityTopic.md +1 -1
- package/docs/api/functions/liveGlobalConnectionTopic.md +1 -1
- package/docs/api/functions/mutation.md +1 -1
- package/docs/api/functions/toEntityId.md +1 -1
- package/docs/api/functions/view.md +1 -1
- package/docs/api/index.md +9 -0
- package/docs/api/interfaces/FateThenable.md +3 -3
- package/docs/api/interfaces/Transport.md +7 -7
- package/docs/api/server/classes/FateRequestError.md +5 -5
- package/docs/api/server/drizzle/functions/createDrizzleFate.md +1 -1
- package/docs/api/server/drizzle/functions/createDrizzleSourceAdapter.md +1 -1
- package/docs/api/server/drizzle/functions/createDrizzleSourceRegistry.md +1 -1
- package/docs/api/server/drizzle/type-aliases/DrizzleManyToManyConfig.md +1 -1
- package/docs/api/server/drizzle/type-aliases/DrizzleManyToManyInput.md +1 -1
- package/docs/api/server/drizzle/type-aliases/DrizzleQueryExtra.md +3 -3
- package/docs/api/server/drizzle/type-aliases/DrizzleSourceAdapter.md +9 -9
- package/docs/api/server/drizzle/type-aliases/DrizzleViewConfig.md +1 -1
- package/docs/api/server/functions/bindSourceProcedures.md +1 -1
- package/docs/api/server/functions/computed.md +1 -1
- package/docs/api/server/functions/count.md +1 -1
- package/docs/api/server/functions/createFateFetchHandler.md +1 -1
- package/docs/api/server/functions/createFateServer.md +1 -1
- package/docs/api/server/functions/createHonoFateHandler.md +1 -1
- package/docs/api/server/functions/createLiveEventBus.md +1 -1
- package/docs/api/server/functions/createNestedSourcePlan.md +1 -1
- package/docs/api/server/functions/createResolver.md +1 -1
- package/docs/api/server/functions/createSourcePlan.md +1 -1
- package/docs/api/server/functions/createSourceProcedures.md +1 -1
- package/docs/api/server/functions/dataView.md +1 -1
- package/docs/api/server/functions/field.md +1 -1
- package/docs/api/server/functions/getNestedSelection.md +1 -1
- package/docs/api/server/functions/getScopedArgs.md +1 -1
- package/docs/api/server/functions/hasNestedSelection.md +1 -1
- package/docs/api/server/functions/list.md +1 -1
- package/docs/api/server/functions/refetchSourceById.md +1 -1
- package/docs/api/server/functions/resolveSourceById.md +1 -1
- package/docs/api/server/functions/resolveSourceByIds.md +1 -1
- package/docs/api/server/functions/resolveSourceConnection.md +1 -1
- package/docs/api/server/functions/resolver.md +1 -1
- package/docs/api/server/functions/toPrismaSelect.md +1 -1
- package/docs/api/server/functions/withConnection.md +1 -1
- package/docs/api/server/prisma/functions/createPrismaFate.md +1 -1
- package/docs/api/server/prisma/functions/createPrismaSourceAdapter.md +1 -1
- package/docs/api/server/prisma/functions/createPrismaSourceRegistry.md +1 -1
- package/docs/api/server/prisma/functions/prismaConnectionArgs.md +1 -1
- package/docs/api/server/prisma/type-aliases/PrismaDelegate.md +4 -4
- package/docs/api/server/prisma/type-aliases/PrismaQueryExtra.md +1 -1
- package/docs/api/server/prisma/type-aliases/PrismaSourceAdapter.md +9 -9
- package/docs/api/server/prisma/type-aliases/PrismaViewConfig.md +1 -1
- package/docs/api/server/type-aliases/ComputedField.md +5 -5
- package/docs/api/server/type-aliases/ComputedSelection.md +1 -1
- package/docs/api/server/type-aliases/ConnectionItem.md +3 -3
- package/docs/api/server/type-aliases/ConnectionPagination.md +5 -5
- package/docs/api/server/type-aliases/ConnectionResult.md +3 -3
- package/docs/api/server/type-aliases/CountSelection.md +4 -4
- package/docs/api/server/type-aliases/CountWhere.md +1 -1
- package/docs/api/server/type-aliases/DataViewListOptions.md +2 -2
- package/docs/api/server/type-aliases/DataViewOrderBy.md +1 -1
- package/docs/api/server/type-aliases/DataViewOrderDirection.md +1 -1
- package/docs/api/server/type-aliases/DataViewResult.md +1 -1
- package/docs/api/server/type-aliases/Entity.md +1 -1
- package/docs/api/server/type-aliases/FateServer.md +5 -5
- package/docs/api/server/type-aliases/FateServerManifest.md +1 -1
- package/docs/api/server/type-aliases/FieldSelection.md +3 -3
- package/docs/api/server/type-aliases/LiveConnectionEventType.md +1 -1
- package/docs/api/server/type-aliases/LiveConnectionSourceEvent.md +1 -1
- package/docs/api/server/type-aliases/LiveEventBus.md +1 -1
- package/docs/api/server/type-aliases/LiveEventType.md +1 -1
- package/docs/api/server/type-aliases/LiveSourceEvent.md +1 -1
- package/docs/api/server/type-aliases/NativeFateAPI.md +4 -4
- package/docs/api/server/type-aliases/OrderDirection.md +1 -1
- package/docs/api/server/type-aliases/SourceConfig.md +6 -6
- package/docs/api/server/type-aliases/SourceDefinition.md +6 -6
- package/docs/api/server/type-aliases/SourceOrder.md +1 -1
- package/docs/api/server/type-aliases/SourceOrderField.md +3 -3
- package/docs/api/server/type-aliases/SourcePlan.md +1 -1
- package/docs/api/server/type-aliases/SourcePlanNode.md +1 -1
- package/docs/api/server/type-aliases/SourceRegistry.md +1 -1
- package/docs/api/server/type-aliases/SourceRelation.md +1 -1
- package/docs/api/server/type-aliases/SourceRelationConfig.md +1 -1
- package/docs/api/server/variables/byIdInput.md +1 -1
- package/docs/api/server/variables/connectionArgs.md +1 -1
- package/docs/api/type-aliases/ConnectionMetadata.md +1 -1
- package/docs/api/type-aliases/ConnectionRef.md +1 -1
- package/docs/api/type-aliases/Deferred.md +19 -0
- package/docs/api/type-aliases/DeferredMetadata.md +7 -0
- package/docs/api/type-aliases/DeferredSelection.md +13 -0
- package/docs/api/type-aliases/DeferredSnapshot.md +11 -0
- package/docs/api/type-aliases/Entity.md +2 -2
- package/docs/api/type-aliases/EntityId.md +1 -1
- package/docs/api/type-aliases/FateDehydratedState.md +1 -1
- package/docs/api/type-aliases/FateLiveConnectionEvent.md +1 -1
- package/docs/api/type-aliases/FateLiveEvent.md +1 -1
- package/docs/api/type-aliases/FateMutations.md +1 -1
- package/docs/api/type-aliases/FateOperation.md +1 -1
- package/docs/api/type-aliases/FateProtocolRequest.md +1 -1
- package/docs/api/type-aliases/FateProtocolResponse.md +1 -1
- package/docs/api/type-aliases/FateRecord.md +1 -1
- package/docs/api/type-aliases/FateRoots.md +1 -1
- package/docs/api/type-aliases/GraphQLMutationDefinition.md +1 -1
- package/docs/api/type-aliases/GraphQLMutationInput.md +1 -1
- package/docs/api/type-aliases/GraphQLMutationMap.md +1 -1
- package/docs/api/type-aliases/GraphQLMutationOutput.md +1 -1
- package/docs/api/type-aliases/GraphQLTransportOptions.md +11 -11
- package/docs/api/type-aliases/HydrateOptions.md +1 -1
- package/docs/api/type-aliases/HydrationLimits.md +1 -1
- package/docs/api/type-aliases/InferFateAPI.md +1 -1
- package/docs/api/type-aliases/List.md +1 -1
- package/docs/api/type-aliases/ListEntry.md +1 -1
- package/docs/api/type-aliases/ListItem.md +1 -1
- package/docs/api/type-aliases/Mask.md +1 -1
- package/docs/api/type-aliases/MutationDefinition.md +1 -1
- package/docs/api/type-aliases/MutationEntity.md +1 -1
- package/docs/api/type-aliases/MutationIdentifier.md +1 -1
- package/docs/api/type-aliases/MutationInput.md +1 -1
- package/docs/api/type-aliases/MutationResult.md +1 -1
- package/docs/api/type-aliases/NodesItem.md +1 -1
- package/docs/api/type-aliases/Pagination.md +5 -5
- package/docs/api/type-aliases/Request.md +1 -1
- package/docs/api/type-aliases/RequestMode.md +1 -1
- package/docs/api/type-aliases/RequestOptions.md +1 -1
- package/docs/api/type-aliases/RequestResult.md +1 -1
- package/docs/api/type-aliases/Selection.md +1 -1
- package/docs/api/type-aliases/Snapshot.md +1 -1
- package/docs/api/type-aliases/TypeConfig.md +4 -4
- package/docs/api/type-aliases/View.md +1 -1
- package/docs/api/type-aliases/ViewData.md +1 -1
- package/docs/api/type-aliases/ViewEntity.md +1 -1
- package/docs/api/type-aliases/ViewEntityName.md +1 -1
- package/docs/api/type-aliases/ViewRef.md +1 -1
- package/docs/api/type-aliases/ViewSelection.md +1 -1
- package/docs/api/type-aliases/ViewSnapshot.md +1 -1
- package/docs/api/type-aliases/ViewTag.md +1 -1
- package/docs/api/variables/ConnectionTag.md +1 -1
- package/docs/api/variables/DeferTag.md +7 -0
- package/docs/api/variables/DeferredTag.md +7 -0
- package/docs/guide/actions.md +7 -7
- package/docs/guide/deferred-views.md +62 -0
- package/docs/guide/getting-started.md +21 -3
- package/docs/guide/vue.md +256 -0
- package/docs/index.md +1 -0
- package/docs/{guide/graphql-integration.md → integrations/graphql.md} +53 -6
- package/docs/{guide/server-integration.md → integrations/server.md} +93 -7
- package/docs/{guide/void-integration.md → integrations/void.md} +94 -12
- package/lib/{executor-sLcAqDMs.d.mts → executor-Dh-MLUXc.d.mts} +1 -1
- package/lib/{graphqlTransport-DbRHbYtb.d.mts → graphqlTransport-B3cbhvJX.d.mts} +1 -1
- package/lib/graphqlTransport.d.mts +1 -1
- package/lib/index.d.mts +10 -5
- package/lib/index.mjs +119 -3
- package/lib/{list-CMQOQGdn.d.mts → list-BrL6PD_6.d.mts} +1 -1
- package/lib/list.d.mts +1 -1
- package/lib/{record-BtdKzEGI.d.mts → record-BkvZwvFk.d.mts} +2 -2
- package/lib/server/drizzle.d.mts +2 -2
- package/lib/server/prisma.d.mts +2 -2
- package/lib/server.d.mts +3 -3
- package/lib/{transport-BTgtSwOM.d.mts → transport-BjGm__3t.d.mts} +36 -3
- package/lib/vite.d.mts +1 -1
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# GraphQL Integration
|
|
2
2
|
|
|
3
|
-
_fate_ can use an existing GraphQL API as its transport. This keeps the
|
|
3
|
+
_fate_ can use an existing GraphQL API as its transport. This keeps the adapter APIs, view composition, normalized cache, masking, requests, list views, live views, and mutations the same while replacing the native or tRPC backend with GraphQL operations.
|
|
4
4
|
|
|
5
5
|
Use the GraphQL transport when your backend already exposes GraphQL and you want fate's client model without adding fate's native server protocol.
|
|
6
6
|
|
|
@@ -100,13 +100,15 @@ export const fateGraphQL = {
|
|
|
100
100
|
} as const;
|
|
101
101
|
```
|
|
102
102
|
|
|
103
|
-
The data views describe the fields
|
|
103
|
+
The data views describe the fields client components are allowed to select. `Root` describes the root operations available to `useRequest`. `fateGraphQL.roots` maps those root names to actual GraphQL fields. If the GraphQL field has the same name as the fate root, the `field` entry can be omitted.
|
|
104
104
|
|
|
105
105
|
## Vite Plugin
|
|
106
106
|
|
|
107
107
|
Configure the fate Vite plugin with the GraphQL transport and point it at the mapping module:
|
|
108
108
|
|
|
109
|
-
|
|
109
|
+
::: code-group
|
|
110
|
+
|
|
111
|
+
```tsx [React]
|
|
110
112
|
import { fate } from 'react-fate/vite';
|
|
111
113
|
import { defineConfig } from 'vite';
|
|
112
114
|
|
|
@@ -120,13 +122,33 @@ export default defineConfig({
|
|
|
120
122
|
});
|
|
121
123
|
```
|
|
122
124
|
|
|
125
|
+
```ts [Vue]
|
|
126
|
+
import vue from '@vitejs/plugin-vue';
|
|
127
|
+
import { fate } from 'vue-fate/vite';
|
|
128
|
+
import { defineConfig } from 'vite';
|
|
129
|
+
|
|
130
|
+
export default defineConfig({
|
|
131
|
+
plugins: [
|
|
132
|
+
vue(),
|
|
133
|
+
fate({
|
|
134
|
+
module: './src/fate/graphql.ts',
|
|
135
|
+
transport: 'graphql',
|
|
136
|
+
}),
|
|
137
|
+
],
|
|
138
|
+
});
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
:::
|
|
142
|
+
|
|
123
143
|
The plugin generates a typed `createFateClient` helper from your views, roots, and GraphQL mapping. It also watches the mapping module and the files it imports during development.
|
|
124
144
|
|
|
125
145
|
## Creating a Client
|
|
126
146
|
|
|
127
147
|
Create the client with your GraphQL endpoint and provide it through the `FateClient` provider:
|
|
128
148
|
|
|
129
|
-
|
|
149
|
+
::: code-group
|
|
150
|
+
|
|
151
|
+
```tsx [React]
|
|
130
152
|
import { FateClient } from 'react-fate';
|
|
131
153
|
import { createFateClient } from 'react-fate/client';
|
|
132
154
|
|
|
@@ -142,6 +164,29 @@ export function App() {
|
|
|
142
164
|
}
|
|
143
165
|
```
|
|
144
166
|
|
|
167
|
+
```vue [Vue]
|
|
168
|
+
<script setup lang="ts">
|
|
169
|
+
import { FateClient } from 'vue-fate';
|
|
170
|
+
import { createFateClient } from 'vue-fate/client';
|
|
171
|
+
import AppRoutes from './AppRoutes.vue';
|
|
172
|
+
|
|
173
|
+
const fate = createFateClient({
|
|
174
|
+
headers: () => ({
|
|
175
|
+
authorization: `Bearer ${token}`,
|
|
176
|
+
}),
|
|
177
|
+
url: 'https://api.example.com/graphql',
|
|
178
|
+
});
|
|
179
|
+
</script>
|
|
180
|
+
|
|
181
|
+
<template>
|
|
182
|
+
<FateClient :client="fate">
|
|
183
|
+
<AppRoutes />
|
|
184
|
+
</FateClient>
|
|
185
|
+
</template>
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
:::
|
|
189
|
+
|
|
145
190
|
Use `fetch` when you need to customize credentials or reuse an application fetch wrapper:
|
|
146
191
|
|
|
147
192
|
```tsx
|
|
@@ -157,6 +202,8 @@ const fate = createFateClient({
|
|
|
157
202
|
|
|
158
203
|
GraphQL operations issued in the same microtask are batched into a single GraphQL query or mutation document with aliased fields.
|
|
159
204
|
|
|
205
|
+
Deferred view fields work with the GraphQL transport through the same normalized cache flow as native HTTP: the eager query omits `defer(...)` fields, and `useView`, `useListView`, or `useLiveListView` fetches the missing selection through `nodes(ids:)` when the deferred handle is read. GraphQL `@defer` is the natural wire format for this feature, but fate's GraphQL transport currently expects one JSON result per operation and does not consume incremental multipart patches yet.
|
|
206
|
+
|
|
160
207
|
## Object IDs
|
|
161
208
|
|
|
162
209
|
The transport converts between fate entity IDs and GraphQL node IDs. By default, it sends IDs as `${type}-${id}` and strips that prefix from returned IDs. Override this if your schema uses Relay global IDs, raw database IDs, or another encoding:
|
|
@@ -252,7 +299,7 @@ export const fateGraphQL = {
|
|
|
252
299
|
} as const;
|
|
253
300
|
```
|
|
254
301
|
|
|
255
|
-
|
|
302
|
+
Mutations use the same `mutation(...)` API described in the [Actions Guide](../guide/actions.md). React clients can also expose those mutations as Actions for `useActionState`.
|
|
256
303
|
|
|
257
304
|
## Live Views
|
|
258
305
|
|
|
@@ -295,4 +342,4 @@ const fate = createFateClient({
|
|
|
295
342
|
|
|
296
343
|
The GraphQL transport is intentionally a mapping layer. It does not require `createFateServer`, the Prisma adapter, or the Drizzle adapter. Your GraphQL server remains responsible for authorization, validation, resolver behavior, cursor pagination, and mutation side effects.
|
|
297
344
|
|
|
298
|
-
Use data views to expose only the fields the client should be able to select, keep GraphQL schema authorization in your server, and treat `src/fate/graphql.ts` as the contract between your GraphQL API and fate's
|
|
345
|
+
Use data views to expose only the fields the client should be able to select, keep GraphQL schema authorization in your server, and treat `src/fate/graphql.ts` as the contract between your GraphQL API and fate's client.
|
|
@@ -4,7 +4,7 @@ Until now, we have focused on the client-side API of fate. You'll need a backend
|
|
|
4
4
|
|
|
5
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
|
-
- The [GraphQL transport](graphql
|
|
7
|
+
- The [GraphQL transport](graphql.md), which maps fate views and roots to an existing GraphQL schema.
|
|
8
8
|
|
|
9
9
|
_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.
|
|
10
10
|
|
|
@@ -185,7 +185,9 @@ app.post('/fate/live', handler);
|
|
|
185
185
|
|
|
186
186
|
Configure the Vite plugin with the native transport:
|
|
187
187
|
|
|
188
|
-
|
|
188
|
+
::: code-group
|
|
189
|
+
|
|
190
|
+
```tsx [React]
|
|
189
191
|
import { fate } from 'react-fate/vite';
|
|
190
192
|
import { defineConfig } from 'vite';
|
|
191
193
|
|
|
@@ -199,9 +201,29 @@ export default defineConfig({
|
|
|
199
201
|
});
|
|
200
202
|
```
|
|
201
203
|
|
|
204
|
+
```ts [Vue]
|
|
205
|
+
import vue from '@vitejs/plugin-vue';
|
|
206
|
+
import { fate } from 'vue-fate/vite';
|
|
207
|
+
import { defineConfig } from 'vite';
|
|
208
|
+
|
|
209
|
+
export default defineConfig({
|
|
210
|
+
plugins: [
|
|
211
|
+
vue(),
|
|
212
|
+
fate({
|
|
213
|
+
module: '@your-org/server/fate.ts',
|
|
214
|
+
transport: 'native',
|
|
215
|
+
}),
|
|
216
|
+
],
|
|
217
|
+
});
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
:::
|
|
221
|
+
|
|
202
222
|
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:
|
|
203
223
|
|
|
204
|
-
|
|
224
|
+
::: code-group
|
|
225
|
+
|
|
226
|
+
```tsx [React]
|
|
205
227
|
import { createFateClient } from 'react-fate/client';
|
|
206
228
|
|
|
207
229
|
const client = createFateClient({
|
|
@@ -209,6 +231,16 @@ const client = createFateClient({
|
|
|
209
231
|
});
|
|
210
232
|
```
|
|
211
233
|
|
|
234
|
+
```ts [Vue]
|
|
235
|
+
import { createFateClient } from 'vue-fate/client';
|
|
236
|
+
|
|
237
|
+
const client = createFateClient({
|
|
238
|
+
url: '/fate',
|
|
239
|
+
});
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
:::
|
|
243
|
+
|
|
212
244
|
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.
|
|
213
245
|
|
|
214
246
|
### Custom Queries
|
|
@@ -585,7 +617,9 @@ export * from './views.ts';
|
|
|
585
617
|
|
|
586
618
|
Configure the fate Vite plugin with your server module:
|
|
587
619
|
|
|
588
|
-
|
|
620
|
+
::: code-group
|
|
621
|
+
|
|
622
|
+
```tsx [React]
|
|
589
623
|
import { fate } from 'react-fate/vite';
|
|
590
624
|
import { defineConfig } from 'vite';
|
|
591
625
|
|
|
@@ -598,11 +632,28 @@ export default defineConfig({
|
|
|
598
632
|
});
|
|
599
633
|
```
|
|
600
634
|
|
|
635
|
+
```ts [Vue]
|
|
636
|
+
import vue from '@vitejs/plugin-vue';
|
|
637
|
+
import { fate } from 'vue-fate/vite';
|
|
638
|
+
import { defineConfig } from 'vite';
|
|
639
|
+
|
|
640
|
+
export default defineConfig({
|
|
641
|
+
plugins: [
|
|
642
|
+
vue(),
|
|
643
|
+
fate({
|
|
644
|
+
module: '@your-org/server/trpc/router.ts',
|
|
645
|
+
}),
|
|
646
|
+
],
|
|
647
|
+
});
|
|
648
|
+
```
|
|
649
|
+
|
|
650
|
+
:::
|
|
651
|
+
|
|
601
652
|
_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._
|
|
602
653
|
|
|
603
654
|
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.
|
|
604
655
|
|
|
605
|
-
For a barebones client without
|
|
656
|
+
For a barebones client without a framework adapter, 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.
|
|
606
657
|
|
|
607
658
|
The plugin writes project-local types under `.fate/`. If your TypeScript config does not already include dot-directories, extend the generated config:
|
|
608
659
|
|
|
@@ -614,9 +665,11 @@ The plugin writes project-local types under `.fate/`. If your TypeScript config
|
|
|
614
665
|
|
|
615
666
|
## Creating a _fate_ Client
|
|
616
667
|
|
|
617
|
-
Now that the Vite plugin has connected the types, create a fate client instance and provide it to your
|
|
668
|
+
Now that the Vite plugin has connected the types, create a fate client instance and provide it to your app with the `FateClient` provider:
|
|
618
669
|
|
|
619
|
-
|
|
670
|
+
::: code-group
|
|
671
|
+
|
|
672
|
+
```tsx [React]
|
|
620
673
|
import { httpBatchLink } from '@trpc/client';
|
|
621
674
|
import { FateClient } from 'react-fate';
|
|
622
675
|
import { createFateClient } from 'react-fate/client';
|
|
@@ -642,4 +695,37 @@ export function App() {
|
|
|
642
695
|
}
|
|
643
696
|
```
|
|
644
697
|
|
|
698
|
+
```vue [Vue]
|
|
699
|
+
<script setup lang="ts">
|
|
700
|
+
import { httpBatchLink } from '@trpc/client';
|
|
701
|
+
import { computed } from 'vue';
|
|
702
|
+
import { FateClient } from 'vue-fate';
|
|
703
|
+
import { createFateClient } from 'vue-fate/client';
|
|
704
|
+
import AppRoutes from './AppRoutes.vue';
|
|
705
|
+
|
|
706
|
+
const fate = computed(() =>
|
|
707
|
+
createFateClient({
|
|
708
|
+
links: [
|
|
709
|
+
httpBatchLink({
|
|
710
|
+
fetch: (input, init) =>
|
|
711
|
+
fetch(input, {
|
|
712
|
+
...init,
|
|
713
|
+
credentials: 'include',
|
|
714
|
+
}),
|
|
715
|
+
url: `${env('SERVER_URL')}/trpc`,
|
|
716
|
+
}),
|
|
717
|
+
],
|
|
718
|
+
}),
|
|
719
|
+
);
|
|
720
|
+
</script>
|
|
721
|
+
|
|
722
|
+
<template>
|
|
723
|
+
<FateClient :client="fate">
|
|
724
|
+
<AppRoutes />
|
|
725
|
+
</FateClient>
|
|
726
|
+
</template>
|
|
727
|
+
```
|
|
728
|
+
|
|
729
|
+
:::
|
|
730
|
+
|
|
645
731
|
_And you are all set. Happy building!_
|
|
@@ -7,15 +7,25 @@ setup without copying its adapter glue.
|
|
|
7
7
|
|
|
8
8
|
## Install
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
::: code-group
|
|
11
|
+
|
|
12
|
+
```sh [React]
|
|
13
|
+
pnpm add @nkzw/fate react-fate void-fate void @void/react
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
```sh [Vue]
|
|
17
|
+
pnpm add @nkzw/fate vue-fate void-fate void @void/vue
|
|
12
18
|
```
|
|
13
19
|
|
|
20
|
+
:::
|
|
21
|
+
|
|
14
22
|
## Vite
|
|
15
23
|
|
|
16
|
-
Use the
|
|
24
|
+
Use the framework adapter's Vite plugin with the Void transport:
|
|
17
25
|
|
|
18
|
-
|
|
26
|
+
::: code-group
|
|
27
|
+
|
|
28
|
+
```tsx [React]
|
|
19
29
|
import { voidReact } from '@void/react/plugin';
|
|
20
30
|
import { fate } from 'react-fate/vite';
|
|
21
31
|
import { defineConfig } from 'vite-plus';
|
|
@@ -33,6 +43,26 @@ export default defineConfig({
|
|
|
33
43
|
});
|
|
34
44
|
```
|
|
35
45
|
|
|
46
|
+
```ts [Vue]
|
|
47
|
+
import { voidVue } from '@void/vue/plugin';
|
|
48
|
+
import { fate } from 'vue-fate/vite';
|
|
49
|
+
import { defineConfig } from 'vite-plus';
|
|
50
|
+
import { voidPlugin } from 'void';
|
|
51
|
+
|
|
52
|
+
export default defineConfig({
|
|
53
|
+
plugins: [
|
|
54
|
+
voidPlugin(),
|
|
55
|
+
voidVue(),
|
|
56
|
+
fate({
|
|
57
|
+
module: './src/fate/server.ts',
|
|
58
|
+
transport: 'void',
|
|
59
|
+
}),
|
|
60
|
+
],
|
|
61
|
+
});
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
:::
|
|
65
|
+
|
|
36
66
|
The Void transport uses `/fate` for RPC requests and `/fate-live` for live
|
|
37
67
|
updates by default. In SSR, it calls the exported fate server directly. In the
|
|
38
68
|
browser, it uses fetch and the SSE live endpoint.
|
|
@@ -104,12 +134,14 @@ export const { GET, POST } = defineVoidFateLiveRoute(fateServer, fateLive);
|
|
|
104
134
|
The live route handles `GET /fate-live` SSE connections and `POST /fate-live`
|
|
105
135
|
control messages. `void-fate` does not use WebSockets.
|
|
106
136
|
|
|
107
|
-
##
|
|
137
|
+
## Layout
|
|
108
138
|
|
|
109
|
-
Wrap your app with
|
|
110
|
-
provides the fate client through
|
|
139
|
+
Wrap your app with the Void fate client for your framework. It creates and
|
|
140
|
+
provides the fate client through the matching adapter.
|
|
111
141
|
|
|
112
|
-
|
|
142
|
+
::: code-group
|
|
143
|
+
|
|
144
|
+
```tsx [React]
|
|
113
145
|
import { useShared } from '@void/react';
|
|
114
146
|
import type { ReactNode } from 'react';
|
|
115
147
|
import { VoidFateClient } from 'void-fate/react';
|
|
@@ -128,9 +160,36 @@ export default function Layout({ children }: { children: ReactNode }) {
|
|
|
128
160
|
}
|
|
129
161
|
```
|
|
130
162
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
163
|
+
```vue [Vue]
|
|
164
|
+
<script setup lang="ts">
|
|
165
|
+
import { useShared } from '@void/vue';
|
|
166
|
+
import { computed } from 'vue';
|
|
167
|
+
import { FateClient } from 'vue-fate';
|
|
168
|
+
import { createFateClient } from 'vue-fate/client';
|
|
169
|
+
import type { SharedData } from '../src/lib/shared.ts';
|
|
170
|
+
|
|
171
|
+
const shared = useShared<SharedData>();
|
|
172
|
+
|
|
173
|
+
const fate = computed(() =>
|
|
174
|
+
createFateClient({
|
|
175
|
+
origin: typeof window === 'undefined' ? shared.origin : window.location.origin,
|
|
176
|
+
userId: shared.auth.user?.id,
|
|
177
|
+
}),
|
|
178
|
+
);
|
|
179
|
+
</script>
|
|
180
|
+
|
|
181
|
+
<template>
|
|
182
|
+
<FateClient :client="fate">
|
|
183
|
+
<slot />
|
|
184
|
+
</FateClient>
|
|
185
|
+
</template>
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
:::
|
|
189
|
+
|
|
190
|
+
`userId` is optional, but passing it lets the client be recreated when the
|
|
191
|
+
signed-in user changes. Browser requests include credentials when a `userId` is
|
|
192
|
+
present.
|
|
134
193
|
|
|
135
194
|
## Custom Paths
|
|
136
195
|
|
|
@@ -143,12 +202,35 @@ export const fateLive = createVoidFateLive({
|
|
|
143
202
|
});
|
|
144
203
|
```
|
|
145
204
|
|
|
146
|
-
|
|
205
|
+
::: code-group
|
|
206
|
+
|
|
207
|
+
```tsx [React]
|
|
147
208
|
<VoidFateClient livePath="/custom-fate-live" origin={origin} rpcPath="/custom-fate" userId={userId}>
|
|
148
209
|
{children}
|
|
149
210
|
</VoidFateClient>
|
|
150
211
|
```
|
|
151
212
|
|
|
213
|
+
```vue [Vue]
|
|
214
|
+
<script setup lang="ts">
|
|
215
|
+
const fate = computed(() =>
|
|
216
|
+
createFateClient({
|
|
217
|
+
livePath: '/custom-fate-live',
|
|
218
|
+
origin,
|
|
219
|
+
rpcPath: '/custom-fate',
|
|
220
|
+
userId,
|
|
221
|
+
}),
|
|
222
|
+
);
|
|
223
|
+
</script>
|
|
224
|
+
|
|
225
|
+
<template>
|
|
226
|
+
<FateClient :client="fate">
|
|
227
|
+
<slot />
|
|
228
|
+
</FateClient>
|
|
229
|
+
</template>
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
:::
|
|
233
|
+
|
|
152
234
|
The route helper does not own the route path. Make sure your Void route filename
|
|
153
235
|
or router configuration matches the paths you pass to the client.
|
|
154
236
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { C as MutationShape, f as Entity, j as TypeConfig, t as Transport } from "./transport-BjGm__3t.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/graphqlTransport.d.ts
|
|
4
4
|
type TransportMutations = Record<string, MutationShape>;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as GraphQLTransportOptions, i as GraphQLMutationOutput, n as GraphQLMutationInput, o as createGraphQLTransport, r as GraphQLMutationMap, s as graphqlMutation, t as GraphQLMutationDefinition } from "./graphqlTransport-
|
|
1
|
+
import { a as GraphQLTransportOptions, i as GraphQLMutationOutput, n as GraphQLMutationInput, o as createGraphQLTransport, r as GraphQLMutationMap, s as graphqlMutation, t as GraphQLMutationDefinition } from "./graphqlTransport-B3cbhvJX.mjs";
|
|
2
2
|
export { GraphQLMutationDefinition, GraphQLMutationInput, GraphQLMutationMap, GraphQLMutationOutput, GraphQLTransportOptions, createGraphQLTransport, graphqlMutation };
|
package/lib/index.d.mts
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
|
-
import { A as
|
|
2
|
-
import { a as GraphQLTransportOptions, i as GraphQLMutationOutput, n as GraphQLMutationInput, o as createGraphQLTransport, r as GraphQLMutationMap, s as graphqlMutation, t as GraphQLMutationDefinition } from "./graphqlTransport-
|
|
3
|
-
import { C as liveGlobalConnectionTopic, S as liveEntityTopic, a as FateProtocolRequest, i as FateOperation, n as FateLiveConnectionEvent, o as FateProtocolResponse, r as FateLiveEvent, t as isRecord, u as InferFateAPI, x as liveConnectionTopic } from "./record-
|
|
4
|
-
import { n as getListEntries, t as ListEntry } from "./list-
|
|
1
|
+
import { $ as HydrateOptions, A as Snapshot, B as ViewTag, C as MutationShape, D as RequestResult, E as Request$1, F as ViewEntity, G as mutation, H as __FateSelectionBrand, I as ViewEntityName, J as RequestMode, K as DeferredSnapshot, L as ViewRef, M as TypeName, N as View, O as RootDefinition, P as ViewData, Q as FateDehydratedState, R as ViewSelection, S as MutationResult, T as Pagination, U as isViewTag, V as __FateEntityBrand, W as FateMutations, X as createClient, Y as RequestOptions, Z as getSelectionPlan, _ as Mask, a as ConnectionRef, b as MutationIdentifier, c as Deferred, d as DeferredTag, et as HydrationLimits, f as Entity, g as ListItem, h as FateThenable, i as ConnectionMetadata, j as TypeConfig, k as Selection, l as DeferredMetadata, m as FateRoots, n as createTRPCTransport, o as ConnectionTag, p as EntityId, q as FateClient, r as AnyRecord, s as DeferTag, t as Transport, tt as List, u as DeferredSelection, v as MutationDefinition, w as NodesItem, x as MutationInput, y as MutationEntity, z as ViewSnapshot } from "./transport-BjGm__3t.mjs";
|
|
2
|
+
import { a as GraphQLTransportOptions, i as GraphQLMutationOutput, n as GraphQLMutationInput, o as createGraphQLTransport, r as GraphQLMutationMap, s as graphqlMutation, t as GraphQLMutationDefinition } from "./graphqlTransport-B3cbhvJX.mjs";
|
|
3
|
+
import { C as liveGlobalConnectionTopic, S as liveEntityTopic, a as FateProtocolRequest, i as FateOperation, n as FateLiveConnectionEvent, o as FateProtocolResponse, r as FateLiveEvent, t as isRecord, u as InferFateAPI, x as liveConnectionTopic } from "./record-BkvZwvFk.mjs";
|
|
4
|
+
import { n as getListEntries, t as ListEntry } from "./list-BrL6PD_6.mjs";
|
|
5
5
|
|
|
6
|
+
//#region src/defer.d.ts
|
|
7
|
+
declare function defer<S>(selection: S): DeferredSelection<S>;
|
|
8
|
+
declare const isDeferred: (value: unknown) => value is Deferred<unknown>;
|
|
9
|
+
declare const getDeferredMetadata: <T>(deferred: Deferred<T>) => DeferredMetadata;
|
|
10
|
+
//#endregion
|
|
6
11
|
//#region src/httpTransport.d.ts
|
|
7
12
|
type TransportMutations = Record<string, MutationShape>;
|
|
8
13
|
type EmptyTransportMutations = Record<never, MutationShape>;
|
|
@@ -88,4 +93,4 @@ type SelectionValidation<T extends Entity, S extends Selection<T>> = Exclude<key
|
|
|
88
93
|
*/
|
|
89
94
|
declare function view<T extends Entity>(): <S extends Selection<T>>(select: S & SelectionValidation<T, S>) => View<T, S>;
|
|
90
95
|
//#endregion
|
|
91
|
-
export { type ConnectionMetadata, type ConnectionRef, ConnectionTag, type Entity, type EntityId, FateClient, type FateDehydratedState, type FateLiveConnectionEvent, type FateLiveEvent, type FateMutations, type FateOperation, type FateProtocolRequest, type FateProtocolResponse, type AnyRecord as FateRecord, type FateRoots, type FateThenable, type GraphQLMutationDefinition, type GraphQLMutationInput, type GraphQLMutationMap, type GraphQLMutationOutput, type GraphQLTransportOptions, type HydrateOptions, type HydrationLimits, type InferFateAPI, type List, type ListEntry, type ListItem, type Mask, type MutationDefinition, type MutationEntity, type MutationIdentifier, type MutationInput, type MutationResult, type NodesItem, type Pagination, type Request$1 as Request, type RequestMode, type RequestOptions, type RequestResult, type Selection, type Snapshot, type Transport, type TypeConfig, type View, type ViewData, type ViewEntity, type ViewEntityName, type ViewRef, type ViewSelection, type ViewSnapshot, type ViewTag, clientRoot, createClient, createGraphQLTransport, createHTTPTransport, createTRPCTransport, getListEntries, getSelectionPlan, graphqlMutation, isRecord, isViewTag, liveConnectionTopic, liveEntityTopic, liveGlobalConnectionTopic, mutation, toEntityId, view };
|
|
96
|
+
export { type ConnectionMetadata, type ConnectionRef, ConnectionTag, DeferTag, type Deferred, type DeferredMetadata, type DeferredSelection, type DeferredSnapshot, DeferredTag, type Entity, type EntityId, FateClient, type FateDehydratedState, type FateLiveConnectionEvent, type FateLiveEvent, type FateMutations, type FateOperation, type FateProtocolRequest, type FateProtocolResponse, type AnyRecord as FateRecord, type FateRoots, type FateThenable, type GraphQLMutationDefinition, type GraphQLMutationInput, type GraphQLMutationMap, type GraphQLMutationOutput, type GraphQLTransportOptions, type HydrateOptions, type HydrationLimits, type InferFateAPI, type List, type ListEntry, type ListItem, type Mask, type MutationDefinition, type MutationEntity, type MutationIdentifier, type MutationInput, type MutationResult, type NodesItem, type Pagination, type Request$1 as Request, type RequestMode, type RequestOptions, type RequestResult, type Selection, type Snapshot, type Transport, type TypeConfig, type View, type ViewData, type ViewEntity, type ViewEntityName, type ViewRef, type ViewSelection, type ViewSnapshot, type ViewTag, clientRoot, createClient, createGraphQLTransport, createHTTPTransport, createTRPCTransport, defer, getDeferredMetadata, getListEntries, getSelectionPlan, graphqlMutation, isDeferred, isRecord, isViewTag, liveConnectionTopic, liveEntityTopic, liveGlobalConnectionTopic, mutation, toEntityId, view };
|
package/lib/index.mjs
CHANGED
|
@@ -78,6 +78,10 @@ const ViewsTag = Symbol("__fate__views");
|
|
|
78
78
|
const NodeRefTag = Symbol("__fate__node-ref");
|
|
79
79
|
/** Symbol attached to connection results so pagination helpers can find their metadata. */
|
|
80
80
|
const ConnectionTag = Symbol("__fate__connection");
|
|
81
|
+
/** Symbol used to mark a selection as deferred. */
|
|
82
|
+
const DeferTag = Symbol("__fate__defer");
|
|
83
|
+
/** Symbol attached to deferred values returned from masked views. */
|
|
84
|
+
const DeferredTag = Symbol("__fate__deferred");
|
|
81
85
|
const viewTag = "__fate-view__";
|
|
82
86
|
/** Generates a stable view tag for a view definition. */
|
|
83
87
|
function getViewTag(id) {
|
|
@@ -104,6 +108,16 @@ const RootKind = "__fate__root";
|
|
|
104
108
|
/** Brand used on mutation definitions to mark their identity in the d.ts output. */
|
|
105
109
|
const MutationKind = "__fate__mutation";
|
|
106
110
|
//#endregion
|
|
111
|
+
//#region src/defer.ts
|
|
112
|
+
function defer(selection) {
|
|
113
|
+
return Object.freeze({ [DeferTag]: selection });
|
|
114
|
+
}
|
|
115
|
+
const isDeferredSelection = (value) => typeof value === "object" && value !== null && DeferTag in value;
|
|
116
|
+
const getDeferredSelection = (selection) => selection[DeferTag];
|
|
117
|
+
const createDeferred = (metadata) => Object.freeze({ [DeferredTag]: metadata });
|
|
118
|
+
const isDeferred = (value) => typeof value === "object" && value !== null && DeferredTag in value;
|
|
119
|
+
const getDeferredMetadata = (deferred) => deferred[DeferredTag];
|
|
120
|
+
//#endregion
|
|
107
121
|
//#region src/node-ref.ts
|
|
108
122
|
function createNodeRef(id) {
|
|
109
123
|
const ref = {};
|
|
@@ -706,6 +720,7 @@ const getSelectionPlan = (viewComposition, ref) => {
|
|
|
706
720
|
if (value) paths.add(path);
|
|
707
721
|
continue;
|
|
708
722
|
}
|
|
723
|
+
if (isDeferredSelection(value)) continue;
|
|
709
724
|
if (isViewTag(key)) {
|
|
710
725
|
if (!ref || ref[ViewsTag] && ref[ViewsTag].has(key)) walk(value.select, prefix);
|
|
711
726
|
continue;
|
|
@@ -743,6 +758,9 @@ const getSelectionPlan = (viewComposition, ref) => {
|
|
|
743
758
|
paths
|
|
744
759
|
};
|
|
745
760
|
};
|
|
761
|
+
const getDeferredSelectionPlan = (field, selection) => {
|
|
762
|
+
return getSelectionPlan({ [field]: selection }, null);
|
|
763
|
+
};
|
|
746
764
|
//#endregion
|
|
747
765
|
//#region src/mutation.ts
|
|
748
766
|
/**
|
|
@@ -1796,6 +1814,7 @@ var FateClient = class {
|
|
|
1796
1814
|
this.parentLists = /* @__PURE__ */ new Map();
|
|
1797
1815
|
this.rootLists = /* @__PURE__ */ new Map();
|
|
1798
1816
|
this.pending = /* @__PURE__ */ new Map();
|
|
1817
|
+
this.pendingDeferred = /* @__PURE__ */ new Map();
|
|
1799
1818
|
this.pendingOptimisticMutations = /* @__PURE__ */ new Map();
|
|
1800
1819
|
this.optimisticMasks = /* @__PURE__ */ new Map();
|
|
1801
1820
|
this.optimisticByEntity = /* @__PURE__ */ new Map();
|
|
@@ -2095,6 +2114,63 @@ var FateClient = class {
|
|
|
2095
2114
|
}
|
|
2096
2115
|
return resolveSnapshot();
|
|
2097
2116
|
}
|
|
2117
|
+
readDeferred(deferred) {
|
|
2118
|
+
const metadata = getDeferredMetadata(deferred);
|
|
2119
|
+
const resolvedOwner = this.optimisticEntityResolutions.get(metadata.owner) ?? metadata.owner;
|
|
2120
|
+
const parsedOwner = parseEntityId(resolvedOwner);
|
|
2121
|
+
const ownerRecord = this.store.read(resolvedOwner);
|
|
2122
|
+
const rawOwnerId = ownerRecord && (typeof ownerRecord.id === "string" || typeof ownerRecord.id === "number") ? ownerRecord.id : parsedOwner.id;
|
|
2123
|
+
const ownerType = parsedOwner.type || metadata.type;
|
|
2124
|
+
const plan = getDeferredSelectionPlan(metadata.field, metadata.selection);
|
|
2125
|
+
const selectedPaths = plan.paths;
|
|
2126
|
+
const missing = this.store.missingForSelection(resolvedOwner, selectedPaths);
|
|
2127
|
+
const hasMissingList = () => this.hasMissingDeferredList(resolvedOwner, ownerType, metadata.field, plan);
|
|
2128
|
+
const listMissing = hasMissingList();
|
|
2129
|
+
const fetchPaths = listMissing ? selectedPaths : missing;
|
|
2130
|
+
const resolveSnapshot = () => {
|
|
2131
|
+
const snapshot = this.readViewSelection({ [metadata.field]: metadata.selection }, this.stableRefWithViewNames(ownerType, rawOwnerId, /* @__PURE__ */ new Set()), resolvedOwner, plan);
|
|
2132
|
+
const resolved = {
|
|
2133
|
+
coverage: snapshot.coverage,
|
|
2134
|
+
data: snapshot.data[metadata.field]
|
|
2135
|
+
};
|
|
2136
|
+
return {
|
|
2137
|
+
status: "fulfilled",
|
|
2138
|
+
then: (onfulfilled, onrejected) => Promise.resolve(resolved).then(onfulfilled, onrejected),
|
|
2139
|
+
value: resolved
|
|
2140
|
+
};
|
|
2141
|
+
};
|
|
2142
|
+
if (missing.size === 0 && !listMissing) {
|
|
2143
|
+
this.clearStalledRequestsForEntity(resolvedOwner);
|
|
2144
|
+
return resolveSnapshot();
|
|
2145
|
+
}
|
|
2146
|
+
const key = this.deferredPendingKey(resolvedOwner, fetchPaths, plan, this.deferredSelectionSignature(metadata.selection));
|
|
2147
|
+
if (this.stalledRequests.has(key)) return resolveSnapshot();
|
|
2148
|
+
const pendingOptimistic = this.getPendingOptimisticMutations(resolvedOwner);
|
|
2149
|
+
if (pendingOptimistic) {
|
|
2150
|
+
const pending = this.pendingDeferred.get(key);
|
|
2151
|
+
if (pending) return pending;
|
|
2152
|
+
const promise = Promise.all(pendingOptimistic).then(() => this.readDeferred(deferred)).finally(() => this.pendingDeferred.delete(key));
|
|
2153
|
+
this.pendingDeferred.set(key, promise);
|
|
2154
|
+
return promise;
|
|
2155
|
+
}
|
|
2156
|
+
const pendingPromise = this.pendingDeferred.get(key) || null;
|
|
2157
|
+
if (pendingPromise) return pendingPromise;
|
|
2158
|
+
const promise = this.trackPendingRequest(async () => {
|
|
2159
|
+
try {
|
|
2160
|
+
await this.fetchByIdAndNormalize(ownerType, [rawOwnerId], fetchPaths, plan);
|
|
2161
|
+
if (this.store.missingForSelection(resolvedOwner, selectedPaths).size > 0 || hasMissingList()) {
|
|
2162
|
+
this.stalledRequests.add(key);
|
|
2163
|
+
return resolveSnapshot().value;
|
|
2164
|
+
}
|
|
2165
|
+
this.stalledRequests.delete(key);
|
|
2166
|
+
return this.readDeferred(deferred).value;
|
|
2167
|
+
} finally {
|
|
2168
|
+
this.pendingDeferred.delete(key);
|
|
2169
|
+
}
|
|
2170
|
+
});
|
|
2171
|
+
this.pendingDeferred.set(key, promise);
|
|
2172
|
+
return promise;
|
|
2173
|
+
}
|
|
2098
2174
|
assertLiveViewSupport() {
|
|
2099
2175
|
if (!this.transport.subscribeById) throw new Error(`fate: transport does not support live views. Please provide a 'subscribeById' implementation in your transport.`);
|
|
2100
2176
|
}
|
|
@@ -3138,8 +3214,20 @@ var FateClient = class {
|
|
|
3138
3214
|
target[ViewsTag].add(key);
|
|
3139
3215
|
continue;
|
|
3140
3216
|
}
|
|
3141
|
-
coverageById.set(parentId, (coverageById.get(parentId) ?? /* @__PURE__ */ new Set()).add(key));
|
|
3142
3217
|
const fieldPath = prefix ? `${prefix}.${key}` : key;
|
|
3218
|
+
if (isDeferredSelection(selectionKind)) {
|
|
3219
|
+
const { id, type } = parseEntityId(parentId);
|
|
3220
|
+
coverageById.set(parentId, (coverageById.get(parentId) ?? /* @__PURE__ */ new Set()).add(key));
|
|
3221
|
+
target[key] = createDeferred({
|
|
3222
|
+
field: key,
|
|
3223
|
+
id,
|
|
3224
|
+
owner: parentId,
|
|
3225
|
+
selection: getDeferredSelection(selectionKind),
|
|
3226
|
+
type
|
|
3227
|
+
});
|
|
3228
|
+
continue;
|
|
3229
|
+
}
|
|
3230
|
+
coverageById.set(parentId, (coverageById.get(parentId) ?? /* @__PURE__ */ new Set()).add(key));
|
|
3143
3231
|
const selectionType = typeof selectionKind;
|
|
3144
3232
|
if (selectionType === "boolean" && selectionKind) target[key] = record[key];
|
|
3145
3233
|
else if (selectionKind && selectionType === "object") {
|
|
@@ -3258,7 +3346,9 @@ var FateClient = class {
|
|
|
3258
3346
|
}
|
|
3259
3347
|
};
|
|
3260
3348
|
const data = { __typename: parseEntityId(entityId).type };
|
|
3261
|
-
|
|
3349
|
+
const payloads = getViewPayloads(viewComposition, ref);
|
|
3350
|
+
if (payloads.length === 0 && isRecord(viewComposition)) walk(viewComposition, record, data, entityId, pathPrefix);
|
|
3351
|
+
else for (const viewPayload of payloads) walk(viewPayload.select, record, data, entityId, pathPrefix);
|
|
3262
3352
|
return {
|
|
3263
3353
|
coverage: [...coverageById.entries()],
|
|
3264
3354
|
data
|
|
@@ -3274,6 +3364,32 @@ var FateClient = class {
|
|
|
3274
3364
|
pendingKey(entityId, missingFields) {
|
|
3275
3365
|
return `${this.pendingPrefix(entityId)}${[...missingFields].sort().join("|")}`;
|
|
3276
3366
|
}
|
|
3367
|
+
deferredPendingKey(entityId, fields, plan, selectionSignature) {
|
|
3368
|
+
const args = [...plan.args.entries()].map(([path, entry]) => `${path}:${entry.hash}`).sort().join("|");
|
|
3369
|
+
return `${this.pendingKey(entityId, new Set(fields))}|${args}|${selectionSignature}`;
|
|
3370
|
+
}
|
|
3371
|
+
deferredSelectionSignature(selection) {
|
|
3372
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3373
|
+
const serialize = (value) => {
|
|
3374
|
+
if (value === null || typeof value !== "object") return `${typeof value}:${String(value)}`;
|
|
3375
|
+
if (seen.has(value)) return "[Circular]";
|
|
3376
|
+
seen.add(value);
|
|
3377
|
+
if (Array.isArray(value)) return `[${value.map((item) => serialize(item)).join(",")}]`;
|
|
3378
|
+
return `{${Object.entries(value).sort(([left], [right]) => left.localeCompare(right)).map(([key, entry]) => isViewTag(key) ? `view:${key}` : `${key}:${serialize(entry)}`).join(",")}}`;
|
|
3379
|
+
};
|
|
3380
|
+
return serialize(selection);
|
|
3381
|
+
}
|
|
3382
|
+
hasMissingDeferredList(owner, type, field, plan) {
|
|
3383
|
+
const descriptor = this.types.get(type)?.fields?.[field];
|
|
3384
|
+
if (!descriptor || descriptor === "scalar" || !("listOf" in descriptor)) return false;
|
|
3385
|
+
const fieldArgs = plan.args.get(field);
|
|
3386
|
+
const listState = this.store.getListState(getListKey(owner, field, fieldArgs?.hash));
|
|
3387
|
+
if (!listState) return true;
|
|
3388
|
+
const args = fieldArgs?.value;
|
|
3389
|
+
const paginationArgs = getPaginationArgsInfo(args);
|
|
3390
|
+
if (paginationArgs.hasCursorArg) return true;
|
|
3391
|
+
return !hasConnectionPageCoverage(listState, args, paginationArgs.direction);
|
|
3392
|
+
}
|
|
3277
3393
|
liveSubscriptionKey(entityId, plan) {
|
|
3278
3394
|
return `${entityId}|${[...plan.paths].sort().join("|")}|${[...plan.args.entries()].map(([path, entry]) => `${path}:${entry.hash}`).sort().join("|")}`;
|
|
3279
3395
|
}
|
|
@@ -3808,4 +3924,4 @@ function clientRoot(type) {
|
|
|
3808
3924
|
});
|
|
3809
3925
|
}
|
|
3810
3926
|
//#endregion
|
|
3811
|
-
export { ConnectionTag, FateClient, clientRoot, createClient, createGraphQLTransport, createHTTPTransport, createTRPCTransport, getListEntries, getSelectionPlan, graphqlMutation, isRecord, isViewTag, liveConnectionTopic, liveEntityTopic, liveGlobalConnectionTopic, mutation, toEntityId, view };
|
|
3927
|
+
export { ConnectionTag, DeferTag, DeferredTag, FateClient, clientRoot, createClient, createGraphQLTransport, createHTTPTransport, createTRPCTransport, defer, getDeferredMetadata, getListEntries, getSelectionPlan, graphqlMutation, isDeferred, isRecord, isViewTag, liveConnectionTopic, liveEntityTopic, liveGlobalConnectionTopic, mutation, toEntityId, view };
|