@nkzw/fate 0.1.1 → 0.1.3
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 +4 -19
- package/lib/cli.mjs +11 -11
- package/lib/index.d.mts +1 -2
- package/lib/index.mjs +5 -5
- package/lib/server.d.mts +62 -35
- package/lib/server.mjs +71 -73
- package/lib/types-HECWvGpd.d.mts +1193 -0
- package/package.json +20 -17
- package/lib/types-BiDLhb07.d.mts +0 -620
package/README.md
CHANGED
|
@@ -552,18 +552,9 @@ const PostView = view<Post>()({
|
|
|
552
552
|
Now you can apply the `useListView` hook inside of your `PostCard` component to read the list of comments and load more comments when needed:
|
|
553
553
|
|
|
554
554
|
```tsx
|
|
555
|
-
export function PostCard({
|
|
556
|
-
detail,
|
|
557
|
-
post: postRef,
|
|
558
|
-
}: {
|
|
559
|
-
detail?: boolean;
|
|
560
|
-
post: ViewRef<'Post'>;
|
|
561
|
-
}) {
|
|
555
|
+
export function PostCard({ detail, post: postRef }: { detail?: boolean; post: ViewRef<'Post'> }) {
|
|
562
556
|
const post = useView(PostView, postRef);
|
|
563
|
-
const [comments, loadNext] = useListView(
|
|
564
|
-
CommentConnectionView,
|
|
565
|
-
post.comments,
|
|
566
|
-
);
|
|
557
|
+
const [comments, loadNext] = useListView(CommentConnectionView, post.comments);
|
|
567
558
|
|
|
568
559
|
return (
|
|
569
560
|
<div>
|
|
@@ -615,10 +606,7 @@ If you are not using an async component library, you can use React's `useTransit
|
|
|
615
606
|
const LikeButton = ({ post }: { post: { id: string; likes: number } }) => {
|
|
616
607
|
const fate = useFateClient();
|
|
617
608
|
const [, startTransition] = useTransition();
|
|
618
|
-
const [result, like, isPending] = useActionState(
|
|
619
|
-
fate.actions.post.like,
|
|
620
|
-
null,
|
|
621
|
-
);
|
|
609
|
+
const [result, like, isPending] = useActionState(fate.actions.post.like, null);
|
|
622
610
|
|
|
623
611
|
return (
|
|
624
612
|
<button
|
|
@@ -805,10 +793,7 @@ const [result, like] = useActionState(fate.actions.post.like, null);
|
|
|
805
793
|
useEffect(() => {
|
|
806
794
|
if (result?.error) {
|
|
807
795
|
// Reset the action state after 3 seconds.
|
|
808
|
-
const timeout = setTimeout(
|
|
809
|
-
() => startTransition(() => like('reset')),
|
|
810
|
-
3000,
|
|
811
|
-
);
|
|
796
|
+
const timeout = setTimeout(() => startTransition(() => like('reset')), 3000);
|
|
812
797
|
return () => clearTimeout(timeout);
|
|
813
798
|
}
|
|
814
799
|
}, [like, result]);
|
package/lib/cli.mjs
CHANGED
|
@@ -130,22 +130,22 @@ const generate = async () => {
|
|
|
130
130
|
});
|
|
131
131
|
continue;
|
|
132
132
|
}
|
|
133
|
-
for (const [queryName, root
|
|
134
|
-
if (root
|
|
135
|
-
if (procedureName === root
|
|
133
|
+
for (const [queryName, root] of routerRoots) {
|
|
134
|
+
if (root.kind !== "query") continue;
|
|
135
|
+
if (procedureName === root.procedure && type === "query") queryEntries.push({
|
|
136
136
|
name: queryName,
|
|
137
|
-
procedure: root
|
|
137
|
+
procedure: root.procedure,
|
|
138
138
|
router,
|
|
139
|
-
type: root
|
|
139
|
+
type: root.type
|
|
140
140
|
});
|
|
141
141
|
}
|
|
142
|
-
for (const [listName, root
|
|
143
|
-
if (root
|
|
144
|
-
if (procedureName === root
|
|
142
|
+
for (const [listName, root] of routerRoots) {
|
|
143
|
+
if (root.kind !== "list") continue;
|
|
144
|
+
if (procedureName === root.procedure && type === "query") listEntries.push({
|
|
145
145
|
list: listName,
|
|
146
|
-
procedure: root
|
|
146
|
+
procedure: root.procedure,
|
|
147
147
|
router,
|
|
148
|
-
type: root
|
|
148
|
+
type: root.type
|
|
149
149
|
});
|
|
150
150
|
}
|
|
151
151
|
}
|
|
@@ -203,7 +203,7 @@ const generate = async () => {
|
|
|
203
203
|
const source = `// @generated by \`pnpm fate generate\`
|
|
204
204
|
${typeImports}
|
|
205
205
|
import { createTRPCProxyClient } from '@trpc/client';
|
|
206
|
-
import { inferRouterInputs, inferRouterOutputs } from '@trpc/server';
|
|
206
|
+
import type { inferRouterInputs, inferRouterOutputs } from '@trpc/server';
|
|
207
207
|
import { clientRoot, createClient, createTRPCTransport, mutation } from 'react-fate';
|
|
208
208
|
|
|
209
209
|
type TRPCClientType = ReturnType<typeof createTRPCProxyClient<AppRouter>>;
|
package/lib/index.d.mts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { A as ViewSelection, B as RequestOptions, C as TypeConfig, D as ViewEntity, E as ViewData, F as isViewTag, H as Transport, I as FateMutations, L as mutation, M as ViewTag, N as __FateEntityBrand, O as ViewEntityName, P as __FateSelectionBrand, R as FateClient, S as Snapshot, T as View, U as createTRPCTransport, V as createClient,
|
|
1
|
+
import { A as ViewSelection, B as RequestOptions, C as TypeConfig, D as ViewEntity, E as ViewData, F as isViewTag, G as getSelectionPlan, H as Transport, I as FateMutations, L as mutation, M as ViewTag, N as __FateEntityBrand, O as ViewEntityName, P as __FateSelectionBrand, R as FateClient, S as Snapshot, T as View, U as createTRPCTransport, V as createClient, _ as Pagination, a as Entity, b as RootDefinition, c as FateThenable, d as MutationDefinition, f as MutationEntity, g as NodesItem, h as MutationResult, i as ConnectionTag, j as ViewSnapshot, k as ViewRef, 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 Request, w as TypeName, x as Selection, y as RequestResult, z as RequestMode } from "./types-HECWvGpd.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/root.d.ts
|
|
4
|
-
|
|
5
4
|
/**
|
|
6
5
|
* Defines a root query for an entity type, capturing the response shape.
|
|
7
6
|
*/
|
package/lib/index.mjs
CHANGED
|
@@ -264,7 +264,7 @@ function intersects(a, b) {
|
|
|
264
264
|
}
|
|
265
265
|
|
|
266
266
|
//#endregion
|
|
267
|
-
//#region ../../node_modules/.pnpm/@trpc+server@11.
|
|
267
|
+
//#region ../../node_modules/.pnpm/@trpc+server@11.8.1_typescript@5.9.3/node_modules/@trpc/server/dist/getErrorShape-vC8mUXJD.mjs
|
|
268
268
|
var __create = Object.create;
|
|
269
269
|
var __defProp = Object.defineProperty;
|
|
270
270
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
@@ -432,17 +432,17 @@ const MutationKind = "__fate__mutation";
|
|
|
432
432
|
/**
|
|
433
433
|
* Collects all view payloads that apply to the given ref.
|
|
434
434
|
*/
|
|
435
|
-
const getViewPayloads = (view
|
|
435
|
+
const getViewPayloads = (view, ref) => {
|
|
436
436
|
const result = [];
|
|
437
|
-
for (const [key, value] of Object.entries(view
|
|
437
|
+
for (const [key, value] of Object.entries(view)) if (isViewTag(key) && (!ref || ref[ViewsTag]?.has(key))) result.push(value);
|
|
438
438
|
return result;
|
|
439
439
|
};
|
|
440
440
|
/**
|
|
441
441
|
* Returns the set of view tags defined on a view composition.
|
|
442
442
|
*/
|
|
443
|
-
const getViewNames = (view
|
|
443
|
+
const getViewNames = (view) => {
|
|
444
444
|
const result = /* @__PURE__ */ new Set();
|
|
445
|
-
for (const key of Object.keys(view
|
|
445
|
+
for (const key of Object.keys(view)) if (isViewTag(key)) result.add(key);
|
|
446
446
|
return result;
|
|
447
447
|
};
|
|
448
448
|
/**
|
package/lib/server.d.mts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { t as AnyRecord } from "./types-
|
|
1
|
+
import { W as QueryProcedure, t as AnyRecord } from "./types-HECWvGpd.mjs";
|
|
2
2
|
import { z } from "zod";
|
|
3
|
-
import { TRPCProcedureBuilder } from "@trpc/server";
|
|
4
3
|
|
|
5
4
|
//#region src/server/dataView.d.ts
|
|
6
5
|
declare const dataViewFieldsKey: unique symbol;
|
|
@@ -11,18 +10,18 @@ type ResolverSelect<Context> = AnyRecord | ((options: {
|
|
|
11
10
|
type Bivariant<Fn extends (...args: Array<any>) => unknown> = {
|
|
12
11
|
bivariance(...args: Parameters<Fn>): ReturnType<Fn>;
|
|
13
12
|
}['bivariance'];
|
|
14
|
-
type ResolverResolve<Item
|
|
15
|
-
type ResolverAuthorize<Item
|
|
13
|
+
type ResolverResolve<Item extends AnyRecord, Result, Context> = Bivariant<(item: Item, context?: Context, args?: AnyRecord) => Promise<Result> | Result>;
|
|
14
|
+
type ResolverAuthorize<Item extends AnyRecord, Context> = Bivariant<(item: Item, context?: Context, args?: AnyRecord) => Promise<boolean> | boolean>;
|
|
16
15
|
/**
|
|
17
16
|
* Field configuration for selecting and resolving a computed value on the backend.
|
|
18
17
|
*/
|
|
19
|
-
type ResolverField<Item
|
|
20
|
-
authorize?: ResolverAuthorize<Item
|
|
18
|
+
type ResolverField<Item extends AnyRecord, Result, Context> = {
|
|
19
|
+
authorize?: ResolverAuthorize<Item, Context>;
|
|
21
20
|
kind: 'resolver';
|
|
22
|
-
resolve: ResolverResolve<Item
|
|
21
|
+
resolve: ResolverResolve<Item, Result, Context>;
|
|
23
22
|
select?: ResolverSelect<Context>;
|
|
24
23
|
};
|
|
25
|
-
type DataField<Item
|
|
24
|
+
type DataField<Item extends AnyRecord> = true | DataView<AnyRecord> | ResolverField<Item, any, any>;
|
|
26
25
|
/**
|
|
27
26
|
* Recursively serializes resolver results for transport across the network.
|
|
28
27
|
*/
|
|
@@ -31,15 +30,15 @@ type Serializable<T> = T extends Date ? string : T extends Array<infer U> ? Arra
|
|
|
31
30
|
* Server-side mirror of a view definition describing how to select and resolve
|
|
32
31
|
* fields when fulfilling a client request.
|
|
33
32
|
*/
|
|
34
|
-
type DataView<Item
|
|
35
|
-
fields: Record<string, DataField<Item
|
|
33
|
+
type DataView<Item extends AnyRecord> = {
|
|
34
|
+
fields: Record<string, DataField<Item>>;
|
|
36
35
|
kind?: 'resolver' | 'list';
|
|
37
36
|
typeName: string;
|
|
38
37
|
};
|
|
39
38
|
/**
|
|
40
39
|
* Convenience type for declaring the fields of a server data view.
|
|
41
40
|
*/
|
|
42
|
-
type DataViewConfig<Item
|
|
41
|
+
type DataViewConfig<Item extends AnyRecord> = Record<string, DataField<Item>>;
|
|
43
42
|
/**
|
|
44
43
|
* Declares a server data view that exposes an object's available fields to the client.
|
|
45
44
|
*
|
|
@@ -49,27 +48,27 @@ type DataViewConfig<Item$1 extends AnyRecord> = Record<string, DataField<Item$1>
|
|
|
49
48
|
* title: true,
|
|
50
49
|
* });
|
|
51
50
|
*/
|
|
52
|
-
declare function dataView<Item
|
|
51
|
+
declare function dataView<Item extends AnyRecord>(typeName: string): <Fields extends DataViewConfig<Item>>(fields: Fields) => DataView<Item> & {
|
|
53
52
|
readonly [dataViewFieldsKey]: Fields;
|
|
54
53
|
};
|
|
55
54
|
/**
|
|
56
55
|
* Marks a data view as a list resolver so the server can respond with
|
|
57
56
|
* connection information.
|
|
58
57
|
*/
|
|
59
|
-
declare const list: <Item
|
|
58
|
+
declare const list: <Item extends AnyRecord>(view: DataView<Item>) => {
|
|
60
59
|
kind: "list";
|
|
61
|
-
fields: Record<string, DataField<Item
|
|
60
|
+
fields: Record<string, DataField<Item>>;
|
|
62
61
|
typeName: string;
|
|
63
62
|
};
|
|
64
63
|
/**
|
|
65
64
|
* Declares a resolver field inside a data view, optionally providing a
|
|
66
65
|
* selection for any data dependencies.
|
|
67
66
|
*/
|
|
68
|
-
declare function resolver<Item
|
|
69
|
-
authorize?: ResolverAuthorize<Item
|
|
70
|
-
resolve: ResolverResolve<Item
|
|
67
|
+
declare function resolver<Item extends AnyRecord, Result = unknown, Context = unknown>(config: {
|
|
68
|
+
authorize?: ResolverAuthorize<Item, Context>;
|
|
69
|
+
resolve: ResolverResolve<Item, Result, Context>;
|
|
71
70
|
select?: ResolverSelect<Context>;
|
|
72
|
-
}): ResolverField<Item
|
|
71
|
+
}): ResolverField<Item, Result, Context>;
|
|
73
72
|
type NonNullish<T> = Exclude<T, null | undefined>;
|
|
74
73
|
type WithNullish<Original, Value> = null extends Original ? undefined extends Original ? Value | null | undefined : Value | null : undefined extends Original ? Value | undefined : Value;
|
|
75
74
|
type ResolverResult<Field> = Field extends ResolverField<AnyRecord, any, any> ? Field['authorize'] extends ResolverAuthorize<any, any> ? Awaited<ReturnType<Field['resolve']>> | null : Awaited<ReturnType<Field['resolve']>> : never;
|
|
@@ -77,7 +76,7 @@ type RelationResult<ItemField, V extends DataView<AnyRecord>> = NonNullish<ItemF
|
|
|
77
76
|
type ViewFieldConfig<V extends DataView<AnyRecord>> = V extends {
|
|
78
77
|
readonly [dataViewFieldsKey]: infer Fields;
|
|
79
78
|
} ? Fields : V['fields'];
|
|
80
|
-
type RawFieldResult<Item
|
|
79
|
+
type RawFieldResult<Item extends AnyRecord, Key extends PropertyKey, Field extends DataField<Item>> = Field extends true ? Key extends keyof Item ? Item[Key] : never : Field extends DataView<infer ChildItem> ? Key extends keyof Item ? RelationResult<Item[Key], DataView<ChildItem>> : never : Field extends ResolverField<Item, any, any> ? ResolverResult<Field> : never;
|
|
81
80
|
type RawDataViewResult<V extends DataView<AnyRecord>> = V extends DataView<infer Item> ? { [K in keyof ViewFieldConfig<V>]: RawFieldResult<Item, K, ViewFieldConfig<V>[K]> } : never;
|
|
82
81
|
type DataViewResult<V extends DataView<AnyRecord>> = Serializable<RawDataViewResult<V>>;
|
|
83
82
|
type WithTypename<T, Name extends string> = T & {
|
|
@@ -91,7 +90,7 @@ type Entity<TView extends DataView<AnyRecord>, Name extends string, Replacements
|
|
|
91
90
|
* Builds a resolver that applies a client's selection to a server data view,
|
|
92
91
|
* filtering fields, running nested resolvers, and shaping selects.
|
|
93
92
|
*/
|
|
94
|
-
declare function createResolver<Item
|
|
93
|
+
declare function createResolver<Item extends AnyRecord, Context = unknown>({
|
|
95
94
|
args,
|
|
96
95
|
ctx,
|
|
97
96
|
select: initialSelect,
|
|
@@ -100,9 +99,9 @@ declare function createResolver<Item$1 extends AnyRecord, Context = unknown>({
|
|
|
100
99
|
args?: AnyRecord;
|
|
101
100
|
ctx?: Context;
|
|
102
101
|
select: Iterable<string>;
|
|
103
|
-
view: DataView<Item
|
|
102
|
+
view: DataView<Item>;
|
|
104
103
|
}): {
|
|
105
|
-
resolve: (item: Item
|
|
104
|
+
resolve: (item: Item) => Promise<AnyRecord>;
|
|
106
105
|
resolveMany: (items: Array<AnyRecord>) => Promise<Array<AnyRecord>>;
|
|
107
106
|
select: Record<string, unknown>;
|
|
108
107
|
};
|
|
@@ -117,8 +116,43 @@ type ConnectionCursor = string;
|
|
|
117
116
|
/**
|
|
118
117
|
* Connection item including the node and opaque cursor.
|
|
119
118
|
*/
|
|
120
|
-
|
|
121
|
-
|
|
119
|
+
type ConnectionItem<TNode> = {
|
|
120
|
+
cursor: ConnectionCursor;
|
|
121
|
+
node: TNode;
|
|
122
|
+
};
|
|
123
|
+
/**
|
|
124
|
+
* Pagination metadata returned with connection lists.
|
|
125
|
+
*/
|
|
126
|
+
type ConnectionPagination = {
|
|
127
|
+
hasNext: boolean;
|
|
128
|
+
hasPrevious: boolean;
|
|
129
|
+
nextCursor?: ConnectionCursor;
|
|
130
|
+
previousCursor?: ConnectionCursor;
|
|
131
|
+
};
|
|
132
|
+
/**
|
|
133
|
+
* Connection payload that mirrors what the client expects for list fields.
|
|
134
|
+
*/
|
|
135
|
+
type ConnectionResult<TNode> = {
|
|
136
|
+
items: Array<ConnectionItem<TNode>>;
|
|
137
|
+
pagination: ConnectionPagination;
|
|
138
|
+
};
|
|
139
|
+
type ProcedureLike = {
|
|
140
|
+
input: (schema: any) => {
|
|
141
|
+
query: (resolver: (options: any) => unknown) => unknown;
|
|
142
|
+
};
|
|
143
|
+
};
|
|
144
|
+
type ProcedureContext<TProcedure> = TProcedure extends {
|
|
145
|
+
input: (schema: any) => {
|
|
146
|
+
query: (resolver: (options: {
|
|
147
|
+
ctx: infer TContext;
|
|
148
|
+
}) => unknown) => unknown;
|
|
149
|
+
};
|
|
150
|
+
} ? TContext : unknown;
|
|
151
|
+
type ConnectionProcedure<TInput, TOutput> = QueryProcedure<{
|
|
152
|
+
input: TInput;
|
|
153
|
+
meta: unknown;
|
|
154
|
+
output: TOutput;
|
|
155
|
+
}>;
|
|
122
156
|
type QueryFn<TContext, TItem, TInput extends ConnectionInput> = (options: {
|
|
123
157
|
ctx: TContext;
|
|
124
158
|
cursor?: ConnectionCursor;
|
|
@@ -144,24 +178,17 @@ declare const connectionInput: z.ZodObject<{
|
|
|
144
178
|
* Converts an array of nodes into a list view connection result the client
|
|
145
179
|
* can normalize.
|
|
146
180
|
*/
|
|
147
|
-
|
|
148
181
|
/**
|
|
149
182
|
* Wraps a tRPC procedure to handle cursor-based pagination with consistent
|
|
150
183
|
* connection semantics.
|
|
151
184
|
*/
|
|
152
|
-
declare
|
|
153
|
-
defaultSize,
|
|
154
|
-
getCursor,
|
|
155
|
-
input: additionalInput,
|
|
156
|
-
map,
|
|
157
|
-
query
|
|
158
|
-
}: {
|
|
185
|
+
declare function withConnection<TProcedure extends ProcedureLike>(procedure: TProcedure): <TItem, TNode = TItem, TAdditionalInput extends AdditionalInputSchema | undefined = undefined>(options: {
|
|
159
186
|
defaultSize?: number;
|
|
160
187
|
getCursor?: (node: TNode) => ConnectionCursor;
|
|
161
188
|
input?: TAdditionalInput;
|
|
162
|
-
map?: MapFn<
|
|
163
|
-
query: QueryFn<
|
|
164
|
-
}) =>
|
|
189
|
+
map?: MapFn<ProcedureContext<TProcedure>, TItem, TNode, ConnectionInputWithAdditional<TAdditionalInput>>;
|
|
190
|
+
query: QueryFn<ProcedureContext<TProcedure>, TItem, ConnectionInputWithAdditional<TAdditionalInput>>;
|
|
191
|
+
}) => ConnectionProcedure<ConnectionInputWithAdditional<TAdditionalInput>, ConnectionResult<TNode>>;
|
|
165
192
|
//#endregion
|
|
166
193
|
//#region src/server/input.d.ts
|
|
167
194
|
declare const byIdInput: z.ZodObject<{
|
package/lib/server.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as isRecord } from "./record-DnhZuvUe.mjs";
|
|
1
|
+
import { t as isRecord$1 } from "./record-DnhZuvUe.mjs";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
|
|
4
4
|
//#region src/server/prismaSelect.ts
|
|
@@ -14,7 +14,7 @@ const toPrismaArgs = (args$1) => {
|
|
|
14
14
|
}
|
|
15
15
|
return prismaArgs;
|
|
16
16
|
};
|
|
17
|
-
const isRecord
|
|
17
|
+
const isRecord = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
18
18
|
/**
|
|
19
19
|
* Narrows nested args to the slice relevant for a particular selection path.
|
|
20
20
|
*/
|
|
@@ -23,10 +23,10 @@ function getScopedArgs(args$1, path) {
|
|
|
23
23
|
const segments = path.split(".");
|
|
24
24
|
let current = args$1;
|
|
25
25
|
for (const segment of segments) {
|
|
26
|
-
if (!isRecord
|
|
26
|
+
if (!isRecord(current)) return;
|
|
27
27
|
current = current[segment];
|
|
28
28
|
}
|
|
29
|
-
return isRecord
|
|
29
|
+
return isRecord(current) ? current : void 0;
|
|
30
30
|
}
|
|
31
31
|
/**
|
|
32
32
|
* Builds a Prisma `select` object from flattened selection paths and optional
|
|
@@ -80,14 +80,14 @@ const paginationArgsSchema = z.strictObject({
|
|
|
80
80
|
first: z.number().int().positive().optional(),
|
|
81
81
|
last: z.number().int().positive().optional()
|
|
82
82
|
}).partial().refine(({ after, before }) => !(after && before), "Connection args can't include both 'after' and 'before'.").refine(({ first, last }) => !(first && last), "Connection args can't include both 'first' and 'last'.").refine(({ before, last }) => !last || before !== void 0, "Connection args using 'last' must also include 'before'.");
|
|
83
|
-
const extractPaginationArgs = (args
|
|
83
|
+
const extractPaginationArgs = (args) => args ? Object.fromEntries(Object.entries(args).filter(([key]) => paginationArgKeys.has(key))) : {};
|
|
84
84
|
/**
|
|
85
85
|
* Converts an array of nodes into a list view connection result the client
|
|
86
86
|
* can normalize.
|
|
87
87
|
*/
|
|
88
|
-
function arrayToConnection(nodes, { args
|
|
88
|
+
function arrayToConnection(nodes, { args, getCursor = (node) => String(node.id) } = {}) {
|
|
89
89
|
if (!nodes) return;
|
|
90
|
-
const paginationArgs = paginationArgsSchema.parse(extractPaginationArgs(args
|
|
90
|
+
const paginationArgs = paginationArgsSchema.parse(extractPaginationArgs(args));
|
|
91
91
|
if (Object.keys(paginationArgs).length === 0) return {
|
|
92
92
|
items: nodes.map((node) => ({
|
|
93
93
|
cursor: getCursor(node),
|
|
@@ -130,8 +130,8 @@ const assignIfChanged = (current, key, next, existing) => {
|
|
|
130
130
|
current[key] = next;
|
|
131
131
|
return current;
|
|
132
132
|
};
|
|
133
|
-
function toConnectionResult({ args
|
|
134
|
-
if (!isRecord(item)) return item;
|
|
133
|
+
function toConnectionResult({ args, item, path, view }) {
|
|
134
|
+
if (!isRecord$1(item)) return item;
|
|
135
135
|
let result = null;
|
|
136
136
|
const base = () => result ? {
|
|
137
137
|
...item,
|
|
@@ -144,17 +144,17 @@ function toConnectionResult({ args: args$1, item, path, view }) {
|
|
|
144
144
|
if (config.kind === "list") {
|
|
145
145
|
if (!Array.isArray(current)) continue;
|
|
146
146
|
const connection = arrayToConnection(current.map((item$1) => toConnectionResult({
|
|
147
|
-
args
|
|
147
|
+
args,
|
|
148
148
|
item: item$1,
|
|
149
149
|
path: nextPath,
|
|
150
150
|
view: config
|
|
151
|
-
})), { args: getScopedArgs(args
|
|
151
|
+
})), { args: getScopedArgs(args, nextPath) });
|
|
152
152
|
result = assignIfChanged(result, field, connection, current);
|
|
153
153
|
continue;
|
|
154
154
|
}
|
|
155
155
|
if (Array.isArray(current)) {
|
|
156
156
|
const wrapped = current.map((entry) => toConnectionResult({
|
|
157
|
-
args
|
|
157
|
+
args,
|
|
158
158
|
item: entry,
|
|
159
159
|
path: nextPath,
|
|
160
160
|
view: config
|
|
@@ -162,9 +162,9 @@ function toConnectionResult({ args: args$1, item, path, view }) {
|
|
|
162
162
|
result = assignIfChanged(result, field, wrapped, current);
|
|
163
163
|
continue;
|
|
164
164
|
}
|
|
165
|
-
if (isRecord(current)) {
|
|
165
|
+
if (isRecord$1(current)) {
|
|
166
166
|
const wrapped = toConnectionResult({
|
|
167
|
-
args
|
|
167
|
+
args,
|
|
168
168
|
item: current,
|
|
169
169
|
path: nextPath,
|
|
170
170
|
view: config
|
|
@@ -177,50 +177,48 @@ function toConnectionResult({ args: args$1, item, path, view }) {
|
|
|
177
177
|
...result
|
|
178
178
|
} : item;
|
|
179
179
|
}
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
const
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
180
|
+
function withConnection(procedure) {
|
|
181
|
+
return ({ defaultSize = 20, getCursor = (node) => node.id, input: additionalInput, map, query }) => {
|
|
182
|
+
const inputSchema = additionalInput ? connectionInput.extend({ args: connectionArgs.and(additionalInput) }) : connectionInput;
|
|
183
|
+
return procedure.input(inputSchema).query(async (resolverOptions) => {
|
|
184
|
+
const { ctx, input } = resolverOptions;
|
|
185
|
+
const paginationArgs = paginationArgsSchema.parse(extractPaginationArgs(input.args));
|
|
186
|
+
const isBackward = paginationArgs.before !== void 0 || paginationArgs.last !== void 0;
|
|
187
|
+
const cursor = isBackward ? paginationArgs.before : paginationArgs.after;
|
|
188
|
+
const direction = isBackward ? "backward" : "forward";
|
|
189
|
+
const pageSize = paginationArgs.first ?? paginationArgs.last ?? defaultSize;
|
|
190
|
+
const rawItems = await query({
|
|
191
|
+
ctx,
|
|
192
|
+
cursor,
|
|
193
|
+
direction,
|
|
194
|
+
input,
|
|
195
|
+
skip: cursor ? 1 : void 0,
|
|
196
|
+
take: pageSize + 1
|
|
197
|
+
});
|
|
198
|
+
const hasMore = rawItems.length > pageSize;
|
|
199
|
+
const limitedItems = isBackward ? rawItems.slice(Math.max(0, rawItems.length - pageSize)) : rawItems.slice(0, pageSize);
|
|
200
|
+
const items = (map ? await map({
|
|
201
|
+
ctx,
|
|
202
|
+
input,
|
|
203
|
+
items: limitedItems
|
|
204
|
+
}) : limitedItems).map((node) => ({
|
|
205
|
+
cursor: getCursor(node),
|
|
206
|
+
node
|
|
207
|
+
}));
|
|
208
|
+
const firstItem = items[0];
|
|
209
|
+
const lastItem = items.at(-1);
|
|
210
|
+
return {
|
|
211
|
+
items,
|
|
212
|
+
pagination: {
|
|
213
|
+
hasNext: isBackward ? Boolean(cursor) : hasMore,
|
|
214
|
+
hasPrevious: isBackward ? hasMore : Boolean(cursor),
|
|
215
|
+
nextCursor: lastItem?.cursor,
|
|
216
|
+
previousCursor: (isBackward ? hasMore : Boolean(cursor)) ? firstItem?.cursor : void 0
|
|
217
|
+
}
|
|
218
|
+
};
|
|
200
219
|
});
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
const items = (map ? await map({
|
|
204
|
-
ctx,
|
|
205
|
-
input,
|
|
206
|
-
items: limitedItems
|
|
207
|
-
}) : limitedItems).map((node) => ({
|
|
208
|
-
cursor: getCursor(node),
|
|
209
|
-
node
|
|
210
|
-
}));
|
|
211
|
-
const firstItem = items[0];
|
|
212
|
-
const lastItem = items.at(-1);
|
|
213
|
-
return {
|
|
214
|
-
items,
|
|
215
|
-
pagination: {
|
|
216
|
-
hasNext: isBackward ? Boolean(cursor) : hasMore,
|
|
217
|
-
hasPrevious: isBackward ? hasMore : Boolean(cursor),
|
|
218
|
-
nextCursor: lastItem?.cursor,
|
|
219
|
-
previousCursor: (isBackward ? hasMore : Boolean(cursor)) ? firstItem?.cursor : void 0
|
|
220
|
-
}
|
|
221
|
-
};
|
|
222
|
-
});
|
|
223
|
-
};
|
|
220
|
+
};
|
|
221
|
+
}
|
|
224
222
|
|
|
225
223
|
//#endregion
|
|
226
224
|
//#region src/server/dataView.ts
|
|
@@ -266,7 +264,7 @@ function resolver(config) {
|
|
|
266
264
|
const isResolverField = (field) => Boolean(field) && typeof field === "object" && "kind" in field && field.kind === "resolver";
|
|
267
265
|
const isDataViewField = (field) => Boolean(field) && typeof field === "object" && "fields" in field;
|
|
268
266
|
const filterToViewFields = (item, view, selectedPaths, prefix = null) => {
|
|
269
|
-
if (!isRecord(item)) return item;
|
|
267
|
+
if (!isRecord$1(item)) return item;
|
|
270
268
|
const filtered = {};
|
|
271
269
|
for (const [field, config] of Object.entries(view.fields)) {
|
|
272
270
|
const path = prefix ? `${prefix}.${field}` : field;
|
|
@@ -282,10 +280,10 @@ const filterToViewFields = (item, view, selectedPaths, prefix = null) => {
|
|
|
282
280
|
const value = item[field];
|
|
283
281
|
if (isDataViewField(config)) {
|
|
284
282
|
if (Array.isArray(value)) {
|
|
285
|
-
filtered[field] = value.map((entry) => isRecord(entry) ? filterToViewFields(entry, config, selectedPaths, path) : entry);
|
|
283
|
+
filtered[field] = value.map((entry) => isRecord$1(entry) ? filterToViewFields(entry, config, selectedPaths, path) : entry);
|
|
286
284
|
continue;
|
|
287
285
|
}
|
|
288
|
-
if (isRecord(value)) {
|
|
286
|
+
if (isRecord$1(value)) {
|
|
289
287
|
filtered[field] = filterToViewFields(value, config, selectedPaths, path);
|
|
290
288
|
continue;
|
|
291
289
|
}
|
|
@@ -297,7 +295,7 @@ const filterToViewFields = (item, view, selectedPaths, prefix = null) => {
|
|
|
297
295
|
const mergeObject = (target, source) => {
|
|
298
296
|
for (const [key, value] of Object.entries(source)) {
|
|
299
297
|
const existing = target[key];
|
|
300
|
-
if (isRecord(existing) && isRecord(value)) {
|
|
298
|
+
if (isRecord$1(existing) && isRecord$1(value)) {
|
|
301
299
|
mergeObject(existing, value);
|
|
302
300
|
continue;
|
|
303
301
|
}
|
|
@@ -310,9 +308,9 @@ const ensureRelationSelect = (select, path) => {
|
|
|
310
308
|
let current = select;
|
|
311
309
|
for (const segment of segments) {
|
|
312
310
|
const existing = current[segment];
|
|
313
|
-
if (isRecord(existing) && "select" in existing) {
|
|
311
|
+
if (isRecord$1(existing) && "select" in existing) {
|
|
314
312
|
const relation$1 = existing;
|
|
315
|
-
if (!isRecord(relation$1.select)) relation$1.select = {};
|
|
313
|
+
if (!isRecord$1(relation$1.select)) relation$1.select = {};
|
|
316
314
|
current = relation$1.select;
|
|
317
315
|
continue;
|
|
318
316
|
}
|
|
@@ -364,32 +362,32 @@ const assignPath = (node, segments, path, selectedPaths, view, allowedPaths) =>
|
|
|
364
362
|
}
|
|
365
363
|
};
|
|
366
364
|
const collectResolvers = (node, select, args$1, context) => {
|
|
367
|
-
for (const resolver
|
|
368
|
-
if (!resolver
|
|
369
|
-
const addition = typeof resolver
|
|
365
|
+
for (const resolver of node.resolvers.values()) {
|
|
366
|
+
if (!resolver.select) continue;
|
|
367
|
+
const addition = typeof resolver.select === "function" ? resolver.select({
|
|
370
368
|
args: args$1,
|
|
371
369
|
context
|
|
372
|
-
}) : resolver
|
|
373
|
-
if (addition && isRecord(addition)) mergeObject(ensureRelationSelect(select, node.path), addition);
|
|
370
|
+
}) : resolver.select;
|
|
371
|
+
if (addition && isRecord$1(addition)) mergeObject(ensureRelationSelect(select, node.path), addition);
|
|
374
372
|
}
|
|
375
373
|
for (const relation of node.relations.values()) collectResolvers(relation, select, args$1, context);
|
|
376
374
|
};
|
|
377
375
|
const resolveNode = async ({ item, node, options: resolverOptions }) => {
|
|
378
|
-
if (!isRecord(item)) return item;
|
|
376
|
+
if (!isRecord$1(item)) return item;
|
|
379
377
|
let result = null;
|
|
380
378
|
const assign = (key, value) => {
|
|
381
379
|
if (!result) result = { ...item };
|
|
382
380
|
result[key] = value;
|
|
383
381
|
};
|
|
384
382
|
const getItem = () => result ?? item;
|
|
385
|
-
for (const [field, resolver
|
|
386
|
-
if (resolver
|
|
387
|
-
if (!await resolver
|
|
383
|
+
for (const [field, resolver] of node.resolvers) {
|
|
384
|
+
if (resolver.authorize) {
|
|
385
|
+
if (!await resolver.authorize(getItem(), resolverOptions.context, resolverOptions.args)) {
|
|
388
386
|
assign(field, null);
|
|
389
387
|
continue;
|
|
390
388
|
}
|
|
391
389
|
}
|
|
392
|
-
const value = await resolver
|
|
390
|
+
const value = await resolver.resolve(getItem(), resolverOptions.context, resolverOptions.args);
|
|
393
391
|
if (value !== void 0) assign(field, value);
|
|
394
392
|
}
|
|
395
393
|
for (const [field, relationNode] of node.relations) {
|