@nkzw/fate 1.0.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/classes/FateClient.md +36 -36
- package/docs/api/functions/clientRoot.md +1 -1
- package/docs/api/functions/createClient.md +1 -1
- package/docs/api/functions/createHTTPTransport.md +1 -1
- package/docs/api/functions/createTRPCTransport.md +1 -1
- package/docs/api/functions/getListEntries.md +1 -1
- package/docs/api/functions/getSelectionPlan.md +1 -1
- 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/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/index.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 +2 -2
- 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 +2 -2
- 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/index.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/Entity.md +2 -2
- package/docs/api/type-aliases/EntityId.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/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/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/lib/index.d.mts +1 -1
- package/lib/index.mjs +5 -4
- package/lib/{record-ENh92gyb.d.mts → record-CirbfZWX.d.mts} +2 -1
- package/lib/server/drizzle.d.mts +2 -0
- package/lib/server/drizzle.mjs +21 -5
- package/lib/server/prisma.mjs +2 -2
- package/lib/server.d.mts +2 -2
- package/lib/server.mjs +39 -8
- package/lib/{sourceRouter-Bwpkr062.mjs → sourceRouter-C22uFcZg.mjs} +1 -1
- package/package.json +1 -1
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> **TypeConfig** = `object`
|
|
4
4
|
|
|
5
|
-
Defined in: [packages/fate/src/types.ts:117](https://github.com/nkzw-tech/fate/blob/
|
|
5
|
+
Defined in: [packages/fate/src/types.ts:117](https://github.com/nkzw-tech/fate/blob/641aeb0396b93477dc2e75fdb2c29a4c2b62a272/packages/fate/src/types.ts#L117)
|
|
6
6
|
|
|
7
7
|
Configuration for a server entity type used by the client cache.
|
|
8
8
|
|
|
@@ -12,7 +12,7 @@ Configuration for a server entity type used by the client cache.
|
|
|
12
12
|
|
|
13
13
|
> `optional` **fields?**: `Record`\<`string`, `RelationDescriptor`\>
|
|
14
14
|
|
|
15
|
-
Defined in: [packages/fate/src/types.ts:118](https://github.com/nkzw-tech/fate/blob/
|
|
15
|
+
Defined in: [packages/fate/src/types.ts:118](https://github.com/nkzw-tech/fate/blob/641aeb0396b93477dc2e75fdb2c29a4c2b62a272/packages/fate/src/types.ts#L118)
|
|
16
16
|
|
|
17
17
|
***
|
|
18
18
|
|
|
@@ -20,7 +20,7 @@ Defined in: [packages/fate/src/types.ts:118](https://github.com/nkzw-tech/fate/b
|
|
|
20
20
|
|
|
21
21
|
> **getId**: (`record`) => `string` \| `number`
|
|
22
22
|
|
|
23
|
-
Defined in: [packages/fate/src/types.ts:119](https://github.com/nkzw-tech/fate/blob/
|
|
23
|
+
Defined in: [packages/fate/src/types.ts:119](https://github.com/nkzw-tech/fate/blob/641aeb0396b93477dc2e75fdb2c29a4c2b62a272/packages/fate/src/types.ts#L119)
|
|
24
24
|
|
|
25
25
|
#### Parameters
|
|
26
26
|
|
|
@@ -38,4 +38,4 @@ Defined in: [packages/fate/src/types.ts:119](https://github.com/nkzw-tech/fate/b
|
|
|
38
38
|
|
|
39
39
|
> **type**: `string`
|
|
40
40
|
|
|
41
|
-
Defined in: [packages/fate/src/types.ts:120](https://github.com/nkzw-tech/fate/blob/
|
|
41
|
+
Defined in: [packages/fate/src/types.ts:120](https://github.com/nkzw-tech/fate/blob/641aeb0396b93477dc2e75fdb2c29a4c2b62a272/packages/fate/src/types.ts#L120)
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> **View**\<`T`, `S`\> = `Readonly`\<\{\[`viewTag`: `` `__fate-view__${string}` ``\]: `Readonly`\<\{ `[ViewKind]`: `true`; `select`: `S`; \}\>; \}\> & `__ViewEntityAnchor`\<`T`\> & `__ViewSelectionAnchor`\<`S`\>
|
|
4
4
|
|
|
5
|
-
Defined in: [packages/fate/src/types.ts:215](https://github.com/nkzw-tech/fate/blob/
|
|
5
|
+
Defined in: [packages/fate/src/types.ts:215](https://github.com/nkzw-tech/fate/blob/641aeb0396b93477dc2e75fdb2c29a4c2b62a272/packages/fate/src/types.ts#L215)
|
|
6
6
|
|
|
7
7
|
Definition of a view over an entity type, including the selection of fields.
|
|
8
8
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> **ViewData**\<`T`, `S`\> = `Readonly`\<`S` *extends* [`Selection`](Selection.md)\<`T`\> ? [`Mask`](Mask.md)\<`T`, `S`\> : `T` & `object`\>
|
|
4
4
|
|
|
5
|
-
Defined in: [packages/fate/src/types.ts:231](https://github.com/nkzw-tech/fate/blob/
|
|
5
|
+
Defined in: [packages/fate/src/types.ts:231](https://github.com/nkzw-tech/fate/blob/641aeb0396b93477dc2e75fdb2c29a4c2b62a272/packages/fate/src/types.ts#L231)
|
|
6
6
|
|
|
7
7
|
Data returned from a resolved view with masking applied and view tags
|
|
8
8
|
attached for downstream composition.
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> **ViewEntity**\<`V`\> = `V` *extends* [`View`](View.md)\<infer T, `any`\> ? `T` : `never`
|
|
4
4
|
|
|
5
|
-
Defined in: [packages/fate/src/types.ts:303](https://github.com/nkzw-tech/fate/blob/
|
|
5
|
+
Defined in: [packages/fate/src/types.ts:303](https://github.com/nkzw-tech/fate/blob/641aeb0396b93477dc2e75fdb2c29a4c2b62a272/packages/fate/src/types.ts#L303)
|
|
6
6
|
|
|
7
7
|
Entity type captured from a view definition.
|
|
8
8
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> **ViewEntityName**\<`V`\> = [`ViewEntity`](ViewEntity.md)\<`V`\>\[`"__typename"`\] & `string`
|
|
4
4
|
|
|
5
|
-
Defined in: [packages/fate/src/types.ts:306](https://github.com/nkzw-tech/fate/blob/
|
|
5
|
+
Defined in: [packages/fate/src/types.ts:306](https://github.com/nkzw-tech/fate/blob/641aeb0396b93477dc2e75fdb2c29a4c2b62a272/packages/fate/src/types.ts#L306)
|
|
6
6
|
|
|
7
7
|
Name of the entity type captured from a view definition.
|
|
8
8
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> **ViewRef**\<`TName`\> = `Readonly`\<\{ `__typename`: `TName`; `[ViewsTag]`: `Set`\<`string`\>; `id`: `string` \| `number`; \}\>
|
|
4
4
|
|
|
5
|
-
Defined in: [packages/fate/src/types.ts:101](https://github.com/nkzw-tech/fate/blob/
|
|
5
|
+
Defined in: [packages/fate/src/types.ts:101](https://github.com/nkzw-tech/fate/blob/641aeb0396b93477dc2e75fdb2c29a4c2b62a272/packages/fate/src/types.ts#L101)
|
|
6
6
|
|
|
7
7
|
Reference to a normalized entity instance that can be resolved against one or more view tags.
|
|
8
8
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> **ViewSelection**\<`V`\> = `V` *extends* `object` ? `S` : `never`
|
|
4
4
|
|
|
5
|
-
Defined in: [packages/fate/src/types.ts:309](https://github.com/nkzw-tech/fate/blob/
|
|
5
|
+
Defined in: [packages/fate/src/types.ts:309](https://github.com/nkzw-tech/fate/blob/641aeb0396b93477dc2e75fdb2c29a4c2b62a272/packages/fate/src/types.ts#L309)
|
|
6
6
|
|
|
7
7
|
Selection captured from a view definition.
|
|
8
8
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> **ViewSnapshot**\<`T`, `S`\> = `Readonly`\<\{ `coverage`: `ReadonlyArray`\<readonly \[[`EntityId`](EntityId.md), `ReadonlySet`\<`string`\>\]\>; `data`: [`ViewData`](ViewData.md)\<`T`, `S`\>; \}\>
|
|
4
4
|
|
|
5
|
-
Defined in: [packages/fate/src/types.ts:241](https://github.com/nkzw-tech/fate/blob/
|
|
5
|
+
Defined in: [packages/fate/src/types.ts:241](https://github.com/nkzw-tech/fate/blob/641aeb0396b93477dc2e75fdb2c29a4c2b62a272/packages/fate/src/types.ts#L241)
|
|
6
6
|
|
|
7
7
|
Snapshot returned by the cache for a view, including the masked data and all
|
|
8
8
|
referenced entity IDs.
|
|
@@ -2,6 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
> **ViewTag** = `` `__fate-view__${string}` ``
|
|
4
4
|
|
|
5
|
-
Defined in: [packages/fate/src/types.ts:49](https://github.com/nkzw-tech/fate/blob/
|
|
5
|
+
Defined in: [packages/fate/src/types.ts:49](https://github.com/nkzw-tech/fate/blob/641aeb0396b93477dc2e75fdb2c29a4c2b62a272/packages/fate/src/types.ts#L49)
|
|
6
6
|
|
|
7
7
|
Unique key that identifies a view composition entry inside a selection or reference.
|
|
@@ -2,6 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
> `const` **ConnectionTag**: *typeof* `ConnectionTag`
|
|
4
4
|
|
|
5
|
-
Defined in: [packages/fate/src/types.ts:20](https://github.com/nkzw-tech/fate/blob/
|
|
5
|
+
Defined in: [packages/fate/src/types.ts:20](https://github.com/nkzw-tech/fate/blob/641aeb0396b93477dc2e75fdb2c29a4c2b62a272/packages/fate/src/types.ts#L20)
|
|
6
6
|
|
|
7
7
|
Symbol attached to connection results so pagination helpers can find their metadata.
|
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/lib/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { A as ViewRef, B as RequestMode, C as Snapshot, D as ViewData, E as View, F as __FateSelectionBrand, G as createTRPCTransport, H as createClient, I as isViewTag, J as getSelectionPlan, L as FateMutations, M as ViewSnapshot, N as ViewTag, O as ViewEntity, P as __FateEntityBrand, R as mutation, S as Selection, T as TypeName, U as List, V as RequestOptions, W as Transport, _ as NodesItem, a as Entity, b as RequestResult, c as FateThenable, d as MutationDefinition, f as MutationEntity, g as MutationShape, h as MutationResult, i as ConnectionTag, j as ViewSelection, k as ViewEntityName, l as ListItem, m as MutationInput, n as ConnectionMetadata, o as EntityId, p as MutationIdentifier, r as ConnectionRef, s as FateRoots, t as AnyRecord, u as Mask, v as Pagination, w as TypeConfig, x as RootDefinition, y as Request$1, z as FateClient } from "./types-Dz46PXr3.mjs";
|
|
2
|
-
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-
|
|
2
|
+
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-CirbfZWX.mjs";
|
|
3
3
|
import { n as getListEntries, t as ListEntry } from "./list-4wRNSVgI.mjs";
|
|
4
4
|
|
|
5
5
|
//#region src/httpTransport.d.ts
|
package/lib/index.mjs
CHANGED
|
@@ -1197,7 +1197,7 @@ const getRequestDescriptorKey = (items) => {
|
|
|
1197
1197
|
parts.push(`query:${item.name}:${item.viewSignature}:${item.argsPayload ? hashArgs(item.argsPayload) : ""}`);
|
|
1198
1198
|
continue;
|
|
1199
1199
|
}
|
|
1200
|
-
if (item.kind === "list") parts.push(`list:${item.name}:${item.viewSignature}:${item.argsPayload ? hashArgs(item.argsPayload) : ""}`);
|
|
1200
|
+
if (item.kind === "list") parts.push(`list:${item.name}:${item.hasItems ? "connection" : "entity"}:${item.viewSignature}:${item.argsPayload ? hashArgs(item.argsPayload) : ""}`);
|
|
1201
1201
|
}
|
|
1202
1202
|
return parts.join("$");
|
|
1203
1203
|
};
|
|
@@ -2156,7 +2156,7 @@ var FateClient = class {
|
|
|
2156
2156
|
...getPaginationMergeInfo(requestArgs),
|
|
2157
2157
|
replace: true
|
|
2158
2158
|
});
|
|
2159
|
-
if (!connection.args) this.registerRootList(connection.type, connection.key);
|
|
2159
|
+
if (!filterConnectionArgs(connection.args)) this.registerRootList(connection.type, connection.key);
|
|
2160
2160
|
this.store.setList(connection.key, nextListState);
|
|
2161
2161
|
return nextListState;
|
|
2162
2162
|
}
|
|
@@ -2231,7 +2231,8 @@ var FateClient = class {
|
|
|
2231
2231
|
const requestKey = descriptor.key;
|
|
2232
2232
|
const existingRequest = this.requests.get(requestKey)?.get(mode);
|
|
2233
2233
|
if (existingRequest) {
|
|
2234
|
-
|
|
2234
|
+
const isFulfilledCacheFirstWithMissingData = mode === "cache-first" && existingRequest.status === "fulfilled" && !this.hasRequestData(existingRequest.descriptor);
|
|
2235
|
+
if (existingRequest.status !== "pending" && (isFulfilledCacheFirstWithMissingData || revalidateExisting && (mode === "network-only" || mode === "stale-while-revalidate" || mode === "cache-first" && existingRequest.status === "rejected"))) this.executeRequestHandle(existingRequest, mode);
|
|
2235
2236
|
return existingRequest;
|
|
2236
2237
|
}
|
|
2237
2238
|
const handle = new FateRequestPromise(descriptor, () => this.getRequestResultFromDescriptor(descriptor));
|
|
@@ -2553,7 +2554,7 @@ var FateClient = class {
|
|
|
2553
2554
|
ids.push(id);
|
|
2554
2555
|
cursors.push(entry.cursor);
|
|
2555
2556
|
}
|
|
2556
|
-
if (!item.argsPayload) this.registerRootList(item.type, item.listKey);
|
|
2557
|
+
if (!filterConnectionArgs(item.argsPayload)) this.registerRootList(item.type, item.listKey);
|
|
2557
2558
|
const previous = this.store.getListState(item.listKey);
|
|
2558
2559
|
this.store.setList(item.listKey, this.mergeListState(previous, ids, cursors, pagination, {
|
|
2559
2560
|
...getPaginationMergeInfo(item.argsPayload),
|
|
@@ -87,7 +87,7 @@ type LiveEventBus = Readonly<{
|
|
|
87
87
|
}) => void;
|
|
88
88
|
}>;
|
|
89
89
|
/**
|
|
90
|
-
* Creates a small in-memory event bus for
|
|
90
|
+
* Creates a small in-memory event bus for fate live view subscriptions.
|
|
91
91
|
*
|
|
92
92
|
* The bus signals that an entity changed and can optionally include changed
|
|
93
93
|
* field paths. The native live handler refetches the selected record, or only
|
|
@@ -146,6 +146,7 @@ type MutationDefinition<Context, Input = unknown, Output = unknown> = {
|
|
|
146
146
|
};
|
|
147
147
|
type LiveConfig = LiveEventBus | {
|
|
148
148
|
bus: LiveEventBus;
|
|
149
|
+
maxQueueSize?: number;
|
|
149
150
|
};
|
|
150
151
|
type ContextOptions<AdapterContext> = {
|
|
151
152
|
adapterContext?: AdapterContext;
|
package/lib/server/drizzle.d.mts
CHANGED
|
@@ -45,6 +45,7 @@ type DrizzleViewConfig<Item extends AnyRecord = AnyRecord, TTable extends Drizzl
|
|
|
45
45
|
type DrizzleViewsInput = Array<DataView<AnyRecord> | DrizzleViewConfig<AnyRecord>> | DataViewModule;
|
|
46
46
|
type DrizzleSourceAdapterOptions<Context> = {
|
|
47
47
|
db: DrizzleDatabaseInput<Context>;
|
|
48
|
+
nestedPaginationConcurrency?: number;
|
|
48
49
|
schema?: Record<string, unknown>;
|
|
49
50
|
views: DrizzleViewsInput;
|
|
50
51
|
};
|
|
@@ -124,6 +125,7 @@ type SourceInput = {
|
|
|
124
125
|
};
|
|
125
126
|
declare function createDrizzleSourceAdapter<Context>({
|
|
126
127
|
db,
|
|
128
|
+
nestedPaginationConcurrency,
|
|
127
129
|
schema,
|
|
128
130
|
views
|
|
129
131
|
}: DrizzleSourceAdapterOptions<Context>): DrizzleSourceAdapter<Context>;
|
package/lib/server/drizzle.mjs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { t as isRecord } from "../record-AeZJC9fd.mjs";
|
|
2
|
-
import { O as withConnection, S as isDataView, c as resolveSourceConnection, d as createSourceDefinitions, f as createSourcePlan, g as attachComputedState, i as createSourceRegistry, l as collectDataViewConfigs, o as resolveSourceById, p as getDataViewSourceConfig, s as resolveSourceByIds, t as bindSourceProcedures } from "../sourceRouter-
|
|
2
|
+
import { O as withConnection, S as isDataView, c as resolveSourceConnection, d as createSourceDefinitions, f as createSourcePlan, g as attachComputedState, i as createSourceRegistry, l as collectDataViewConfigs, o as resolveSourceById, p as getDataViewSourceConfig, s as resolveSourceByIds, t as bindSourceProcedures } from "../sourceRouter-C22uFcZg.mjs";
|
|
3
3
|
import { and, asc, createTableRelationsHelpers, desc, eq, extractTablesRelationalConfig, getTableColumns, gt, inArray, isTable, lt, normalizeRelation, or, sql } from "drizzle-orm";
|
|
4
4
|
//#region src/server/drizzle.ts
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
6
|
+
* fate's Drizzle integration.
|
|
7
7
|
*
|
|
8
8
|
* @example
|
|
9
9
|
* import { createDrizzleFate } from '@nkzw/fate/server/drizzle';
|
|
@@ -29,6 +29,21 @@ const hasPagination = (args) => {
|
|
|
29
29
|
};
|
|
30
30
|
const getConnectionDirection = (args) => args?.before !== void 0 || typeof args?.last === "number" ? "backward" : "forward";
|
|
31
31
|
const getConnectionSize = (fallback, args) => (typeof args?.first === "number" ? args.first : void 0) ?? (typeof args?.last === "number" ? args.last : void 0) ?? fallback;
|
|
32
|
+
const defaultNestedPaginationConcurrency = 10;
|
|
33
|
+
const getNestedPaginationConcurrency = (value) => typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.floor(value) : defaultNestedPaginationConcurrency;
|
|
34
|
+
const mapWithConcurrency = async (items, concurrency, fn) => {
|
|
35
|
+
if (items.length === 0) return [];
|
|
36
|
+
const results = new Array(items.length);
|
|
37
|
+
let nextIndex = 0;
|
|
38
|
+
const workers = Array.from({ length: Math.min(concurrency, items.length) }, async () => {
|
|
39
|
+
while (nextIndex < items.length) {
|
|
40
|
+
const index = nextIndex++;
|
|
41
|
+
results[index] = await fn(items[index], index);
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
await Promise.all(workers);
|
|
45
|
+
return results;
|
|
46
|
+
};
|
|
32
47
|
const buildConnection = ({ cursor, direction, items, pageSize }) => {
|
|
33
48
|
const hasMore = items.length > pageSize;
|
|
34
49
|
const connectionItems = (direction === "forward" ? items.slice(0, pageSize) : items.slice(-pageSize)).map((node) => ({
|
|
@@ -251,7 +266,8 @@ const resolveManyToManyConfig = ({ field, source, sourceRelation, through: throu
|
|
|
251
266
|
table: through.table
|
|
252
267
|
};
|
|
253
268
|
};
|
|
254
|
-
function createDrizzleSourceAdapter({ db, schema, views }) {
|
|
269
|
+
function createDrizzleSourceAdapter({ db, nestedPaginationConcurrency, schema, views }) {
|
|
270
|
+
const nestedPaginationLimit = getNestedPaginationConcurrency(nestedPaginationConcurrency);
|
|
255
271
|
const metadata = getDrizzleSchemaMetadata({
|
|
256
272
|
db,
|
|
257
273
|
schema
|
|
@@ -517,7 +533,7 @@ function createDrizzleSourceAdapter({ db, schema, views }) {
|
|
|
517
533
|
through: throughConfig
|
|
518
534
|
});
|
|
519
535
|
if (hasPagination(relationNode.args)) {
|
|
520
|
-
const connections = await
|
|
536
|
+
const connections = await mapWithConcurrency(items, nestedPaginationLimit, async (item) => [item[sourceRelation.localKey], await fetchManyToManyConnection(item[sourceRelation.localKey], relationNode, sourceRelation, through, ctx)]);
|
|
521
537
|
const connectionByParentKey = new Map(connections);
|
|
522
538
|
for (const item of items) item[field] = connectionByParentKey.get(item[sourceRelation.localKey]);
|
|
523
539
|
continue;
|
|
@@ -534,7 +550,7 @@ function createDrizzleSourceAdapter({ db, schema, views }) {
|
|
|
534
550
|
continue;
|
|
535
551
|
}
|
|
536
552
|
if (hasPagination(relationNode.args)) {
|
|
537
|
-
const connections = await
|
|
553
|
+
const connections = await mapWithConcurrency(items, nestedPaginationLimit, async (item) => [item[sourceRelation.localKey], await fetchManyConnection(item[sourceRelation.localKey], relationNode, sourceRelation, ctx)]);
|
|
538
554
|
const connectionByParentKey = new Map(connections);
|
|
539
555
|
for (const item of items) item[field] = connectionByParentKey.get(item[sourceRelation.localKey]);
|
|
540
556
|
continue;
|
package/lib/server/prisma.mjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { t as isRecord } from "../record-AeZJC9fd.mjs";
|
|
2
|
-
import { O as withConnection, S as isDataView, T as toPrismaSelect, c as resolveSourceConnection, d as createSourceDefinitions, f as createSourcePlan, g as attachComputedState, i as createSourceRegistry, l as collectDataViewConfigs, o as resolveSourceById, p as getDataViewSourceConfig, s as resolveSourceByIds, t as bindSourceProcedures } from "../sourceRouter-
|
|
2
|
+
import { O as withConnection, S as isDataView, T as toPrismaSelect, c as resolveSourceConnection, d as createSourceDefinitions, f as createSourcePlan, g as attachComputedState, i as createSourceRegistry, l as collectDataViewConfigs, o as resolveSourceById, p as getDataViewSourceConfig, s as resolveSourceByIds, t as bindSourceProcedures } from "../sourceRouter-C22uFcZg.mjs";
|
|
3
3
|
//#region src/server/prisma.ts
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
5
|
+
* fate's Prisma integration.
|
|
6
6
|
*
|
|
7
7
|
* @example
|
|
8
8
|
* import { createPrismaFate } from '@nkzw/fate/server/prisma';
|
package/lib/server.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { K as AnyProcedure, t as AnyRecord$1 } from "./types-Dz46PXr3.mjs";
|
|
2
|
-
import { C as liveGlobalConnectionTopic, S as liveEntityTopic, _ as LiveEventBus, b as createLiveEventBus, c as FateServer, d as NativeFateAPI, f as createFateFetchHandler, g as LiveConnectionSourceEvent, h as LiveConnectionEventType, l as FateServerManifest, m as createHonoFateHandler, p as createFateServer, s as FateRequestError, t as isRecord, u as InferFateAPI, v as LiveEventType, x as liveConnectionTopic, y as LiveSourceEvent } from "./record-
|
|
2
|
+
import { C as liveGlobalConnectionTopic, S as liveEntityTopic, _ as LiveEventBus, b as createLiveEventBus, c as FateServer, d as NativeFateAPI, f as createFateFetchHandler, g as LiveConnectionSourceEvent, h as LiveConnectionEventType, l as FateServerManifest, m as createHonoFateHandler, p as createFateServer, s as FateRequestError, t as isRecord, u as InferFateAPI, v as LiveEventType, x as liveConnectionTopic, y as LiveSourceEvent } from "./record-CirbfZWX.mjs";
|
|
3
3
|
import { A as DataViewListOptions, B as dataView, C as connectionArgs, D as CountSelection, E as ComputedSelection, F as FieldSelection, H as list$1, I as ViewPlan, L as computed, M as DataViewOrderDirection, N as DataViewResult, O as CountWhere, P as Entity, R as count, S as ConnectionResult, T as ComputedField, U as resolver, V as field, _ as createSourcePlan, a as resolveSourceConnection, b as ConnectionItem, c as SourceConfig, d as SourceOrderField, f as SourcePlan, g as createNestedSourcePlan, h as SourceRelationConfig, i as resolveSourceByIds, j as DataViewOrderBy, l as SourceDefinition, m as SourceRelation, n as refetchSourceById, p as SourcePlanNode, r as resolveSourceById, s as OrderDirection, t as SourceRegistry, u as SourceOrder, v as getNestedSelection, w as withConnection, x as ConnectionPagination, y as hasNestedSelection, z as createResolver } from "./executor-BqwHdN3n.mjs";
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
|
|
@@ -74,7 +74,7 @@ type SourceProcedureResult<ById extends boolean | undefined, List extends boolea
|
|
|
74
74
|
list: ConnectionProcedureResult<ConnectionProcedure>;
|
|
75
75
|
});
|
|
76
76
|
/**
|
|
77
|
-
* Creates standard `byId` and `list` procedures for a
|
|
77
|
+
* Creates standard `byId` and `list` procedures for a fate source.
|
|
78
78
|
*
|
|
79
79
|
* Use this when a router also has custom mutations or custom queries and you
|
|
80
80
|
* want to spread the generated source procedures into that router.
|