@kubun/graphql 0.16.0 → 0.17.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/lib/context.d.ts +2 -2
- package/lib/relay.d.ts +93 -0
- package/lib/relay.js +197 -0
- package/lib/schema.d.ts +10 -8
- package/lib/schema.js +57 -29
- package/lib/types.d.ts +1 -1
- package/package.json +12 -13
package/lib/context.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ import type { AggregateHaving, AggregateSpec, AggregateValues, DocumentData, Doc
|
|
|
3
3
|
import type { SearchDocumentResult, SearchDocumentsParams, StoredAccessDefaultRow, ViewerReadAccess } from '@kubun/store-graph';
|
|
4
4
|
import type { Tracer } from '@sozai/otel';
|
|
5
5
|
import type { GraphQLResolveInfo } from 'graphql';
|
|
6
|
-
import type { Connection, Edge } from '
|
|
6
|
+
import type { Connection, Edge } from './relay.js';
|
|
7
7
|
import type { AllConnectionArguments, ModelAccessDefaults, UpdateMutationInput } from './types.js';
|
|
8
8
|
/**
|
|
9
9
|
* Event data for document save notifications used by subscriptions.
|
|
@@ -90,7 +90,7 @@ export type AggregateScope = {
|
|
|
90
90
|
};
|
|
91
91
|
/**
|
|
92
92
|
* Connection value returned by {@link QueryContext.resolveConnection}. Extends
|
|
93
|
-
* the
|
|
93
|
+
* the Relay connection (edges/pageInfo are untouched) with the query
|
|
94
94
|
* scope so the `aggregates` field can resolve consistently with the page.
|
|
95
95
|
*/
|
|
96
96
|
export type ScopedConnection = Connection<DocumentNode> & {
|
package/lib/relay.d.ts
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { type GraphQLFieldConfig, type GraphQLFieldConfigArgumentMap, type GraphQLFieldExtensions, type GraphQLFieldResolver, type GraphQLInputFieldConfig, GraphQLInterfaceType, type GraphQLNamedOutputType, GraphQLNonNull, GraphQLObjectType, type GraphQLResolveInfo, type GraphQLTypeResolver, type ThunkObjMap } from 'graphql';
|
|
2
|
+
/**
|
|
3
|
+
* Internal reimplementation of the small slice of `graphql-relay` this package
|
|
4
|
+
* uses (connection args/types, `Node` interface, and Relay-style mutations).
|
|
5
|
+
* The emitted GraphQL types are byte-identical to `graphql-relay@0.10`, which
|
|
6
|
+
* the schema snapshot tests verify.
|
|
7
|
+
*/
|
|
8
|
+
/** A type alias for cursors in this implementation. */
|
|
9
|
+
export type ConnectionCursor = string;
|
|
10
|
+
/** A type describing the arguments a connection field receives in GraphQL. */
|
|
11
|
+
export type ConnectionArguments = {
|
|
12
|
+
before?: ConnectionCursor | null;
|
|
13
|
+
after?: ConnectionCursor | null;
|
|
14
|
+
first?: number | null;
|
|
15
|
+
last?: number | null;
|
|
16
|
+
};
|
|
17
|
+
/** A type designed to be exposed as a `Connection` over GraphQL. */
|
|
18
|
+
export type Connection<T> = {
|
|
19
|
+
edges: Array<Edge<T>>;
|
|
20
|
+
pageInfo: PageInfo;
|
|
21
|
+
};
|
|
22
|
+
/** A type designed to be exposed as an `Edge` over GraphQL. */
|
|
23
|
+
export type Edge<T> = {
|
|
24
|
+
node: T;
|
|
25
|
+
cursor: ConnectionCursor;
|
|
26
|
+
};
|
|
27
|
+
/** A type designed to be exposed as `PageInfo` over GraphQL. */
|
|
28
|
+
export type PageInfo = {
|
|
29
|
+
startCursor: ConnectionCursor | null;
|
|
30
|
+
endCursor: ConnectionCursor | null;
|
|
31
|
+
hasPreviousPage: boolean;
|
|
32
|
+
hasNextPage: boolean;
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Args to include on a field whose return type is a connection type with
|
|
36
|
+
* forward pagination.
|
|
37
|
+
*/
|
|
38
|
+
export declare const forwardConnectionArgs: GraphQLFieldConfigArgumentMap;
|
|
39
|
+
/**
|
|
40
|
+
* Args to include on a field whose return type is a connection type with
|
|
41
|
+
* backward pagination.
|
|
42
|
+
*/
|
|
43
|
+
export declare const backwardConnectionArgs: GraphQLFieldConfigArgumentMap;
|
|
44
|
+
/**
|
|
45
|
+
* Args to include on a field whose return type is a connection type with
|
|
46
|
+
* bidirectional pagination.
|
|
47
|
+
*/
|
|
48
|
+
export declare const connectionArgs: GraphQLFieldConfigArgumentMap;
|
|
49
|
+
export type ConnectionConfig = {
|
|
50
|
+
name?: string;
|
|
51
|
+
nodeType: GraphQLNamedOutputType | GraphQLNonNull<GraphQLNamedOutputType>;
|
|
52
|
+
resolveNode?: GraphQLFieldResolver<any, any>;
|
|
53
|
+
resolveCursor?: GraphQLFieldResolver<any, any>;
|
|
54
|
+
edgeFields?: ThunkObjMap<GraphQLFieldConfig<any, any>>;
|
|
55
|
+
connectionFields?: ThunkObjMap<GraphQLFieldConfig<any, any>>;
|
|
56
|
+
};
|
|
57
|
+
export type GraphQLConnectionDefinitions = {
|
|
58
|
+
edgeType: GraphQLObjectType;
|
|
59
|
+
connectionType: GraphQLObjectType;
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* Returns a GraphQLObjectType for a connection with the given name, and whose
|
|
63
|
+
* nodes are of the specified type.
|
|
64
|
+
*/
|
|
65
|
+
export declare function connectionDefinitions(config: ConnectionConfig): GraphQLConnectionDefinitions;
|
|
66
|
+
export type GraphQLNodeDefinitions<TContext> = {
|
|
67
|
+
nodeInterface: GraphQLInterfaceType;
|
|
68
|
+
nodeField: GraphQLFieldConfig<unknown, TContext>;
|
|
69
|
+
nodesField: GraphQLFieldConfig<unknown, TContext>;
|
|
70
|
+
};
|
|
71
|
+
/**
|
|
72
|
+
* Given a function to map from an ID to an underlying object, and a function to
|
|
73
|
+
* map from an underlying object to its concrete GraphQLObjectType, constructs a
|
|
74
|
+
* `Node` interface plus `node`/`nodes` root field configs.
|
|
75
|
+
*/
|
|
76
|
+
export declare function nodeDefinitions<TContext>(fetchById: (id: string, context: TContext, info: GraphQLResolveInfo) => unknown, typeResolver?: GraphQLTypeResolver<unknown, TContext>): GraphQLNodeDefinitions<TContext>;
|
|
77
|
+
type MutationFn<TInput, TOutput, TContext> = (object: TInput, ctx: TContext, info: GraphQLResolveInfo) => TOutput;
|
|
78
|
+
export type MutationConfig<TInput = any, TOutput = unknown, TContext = unknown> = {
|
|
79
|
+
name: string;
|
|
80
|
+
description?: string;
|
|
81
|
+
deprecationReason?: string;
|
|
82
|
+
extensions?: GraphQLFieldExtensions<unknown, unknown>;
|
|
83
|
+
inputFields: ThunkObjMap<GraphQLInputFieldConfig>;
|
|
84
|
+
outputFields: ThunkObjMap<GraphQLFieldConfig<TOutput, TContext>>;
|
|
85
|
+
mutateAndGetPayload: MutationFn<TInput, Promise<TOutput> | TOutput, TContext>;
|
|
86
|
+
};
|
|
87
|
+
/**
|
|
88
|
+
* Returns a GraphQLFieldConfig for the mutation described by the provided
|
|
89
|
+
* config, wiring the Relay `clientMutationId` round-trip through a generated
|
|
90
|
+
* `${name}Input`/`${name}Payload` pair.
|
|
91
|
+
*/
|
|
92
|
+
export declare function mutationWithClientMutationId<TInput = any, TOutput = unknown, TContext = unknown>(config: MutationConfig<TInput, TOutput, TContext>): GraphQLFieldConfig<unknown, TContext>;
|
|
93
|
+
export {};
|
package/lib/relay.js
ADDED
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import { GraphQLBoolean, GraphQLID, GraphQLInputObjectType, GraphQLInt, GraphQLInterfaceType, GraphQLList, GraphQLNonNull, GraphQLObjectType, GraphQLString, getNamedType, resolveObjMapThunk } from 'graphql';
|
|
2
|
+
/**
|
|
3
|
+
* Args to include on a field whose return type is a connection type with
|
|
4
|
+
* forward pagination.
|
|
5
|
+
*/ export const forwardConnectionArgs = Object.freeze({
|
|
6
|
+
after: {
|
|
7
|
+
type: GraphQLString,
|
|
8
|
+
description: 'Returns the items in the list that come after the specified cursor.'
|
|
9
|
+
},
|
|
10
|
+
first: {
|
|
11
|
+
type: GraphQLInt,
|
|
12
|
+
description: 'Returns the first n items from the list.'
|
|
13
|
+
}
|
|
14
|
+
});
|
|
15
|
+
/**
|
|
16
|
+
* Args to include on a field whose return type is a connection type with
|
|
17
|
+
* backward pagination.
|
|
18
|
+
*/ export const backwardConnectionArgs = Object.freeze({
|
|
19
|
+
before: {
|
|
20
|
+
type: GraphQLString,
|
|
21
|
+
description: 'Returns the items in the list that come before the specified cursor.'
|
|
22
|
+
},
|
|
23
|
+
last: {
|
|
24
|
+
type: GraphQLInt,
|
|
25
|
+
description: 'Returns the last n items from the list.'
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
/**
|
|
29
|
+
* Args to include on a field whose return type is a connection type with
|
|
30
|
+
* bidirectional pagination.
|
|
31
|
+
*/ export const connectionArgs = {
|
|
32
|
+
...forwardConnectionArgs,
|
|
33
|
+
...backwardConnectionArgs
|
|
34
|
+
};
|
|
35
|
+
/** The common page info type shared by every connection. */ const pageInfoType = new GraphQLObjectType({
|
|
36
|
+
name: 'PageInfo',
|
|
37
|
+
description: 'Information about pagination in a connection.',
|
|
38
|
+
fields: ()=>({
|
|
39
|
+
hasNextPage: {
|
|
40
|
+
type: new GraphQLNonNull(GraphQLBoolean),
|
|
41
|
+
description: 'When paginating forwards, are there more items?'
|
|
42
|
+
},
|
|
43
|
+
hasPreviousPage: {
|
|
44
|
+
type: new GraphQLNonNull(GraphQLBoolean),
|
|
45
|
+
description: 'When paginating backwards, are there more items?'
|
|
46
|
+
},
|
|
47
|
+
startCursor: {
|
|
48
|
+
type: GraphQLString,
|
|
49
|
+
description: 'When paginating backwards, the cursor to continue.'
|
|
50
|
+
},
|
|
51
|
+
endCursor: {
|
|
52
|
+
type: GraphQLString,
|
|
53
|
+
description: 'When paginating forwards, the cursor to continue.'
|
|
54
|
+
}
|
|
55
|
+
})
|
|
56
|
+
});
|
|
57
|
+
/**
|
|
58
|
+
* Returns a GraphQLObjectType for a connection with the given name, and whose
|
|
59
|
+
* nodes are of the specified type.
|
|
60
|
+
*/ export function connectionDefinitions(config) {
|
|
61
|
+
const { nodeType } = config;
|
|
62
|
+
const name = config.name ?? getNamedType(nodeType).name;
|
|
63
|
+
const edgeType = new GraphQLObjectType({
|
|
64
|
+
name: `${name}Edge`,
|
|
65
|
+
description: 'An edge in a connection.',
|
|
66
|
+
fields: ()=>({
|
|
67
|
+
node: {
|
|
68
|
+
type: nodeType,
|
|
69
|
+
resolve: config.resolveNode,
|
|
70
|
+
description: 'The item at the end of the edge'
|
|
71
|
+
},
|
|
72
|
+
cursor: {
|
|
73
|
+
type: new GraphQLNonNull(GraphQLString),
|
|
74
|
+
resolve: config.resolveCursor,
|
|
75
|
+
description: 'A cursor for use in pagination'
|
|
76
|
+
},
|
|
77
|
+
...resolveObjMapThunk(config.edgeFields ?? {})
|
|
78
|
+
})
|
|
79
|
+
});
|
|
80
|
+
const connectionType = new GraphQLObjectType({
|
|
81
|
+
name: `${name}Connection`,
|
|
82
|
+
description: 'A connection to a list of items.',
|
|
83
|
+
fields: ()=>({
|
|
84
|
+
pageInfo: {
|
|
85
|
+
type: new GraphQLNonNull(pageInfoType),
|
|
86
|
+
description: 'Information to aid in pagination.'
|
|
87
|
+
},
|
|
88
|
+
edges: {
|
|
89
|
+
type: new GraphQLList(edgeType),
|
|
90
|
+
description: 'A list of edges.'
|
|
91
|
+
},
|
|
92
|
+
...resolveObjMapThunk(config.connectionFields ?? {})
|
|
93
|
+
})
|
|
94
|
+
});
|
|
95
|
+
return {
|
|
96
|
+
edgeType,
|
|
97
|
+
connectionType
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Given a function to map from an ID to an underlying object, and a function to
|
|
102
|
+
* map from an underlying object to its concrete GraphQLObjectType, constructs a
|
|
103
|
+
* `Node` interface plus `node`/`nodes` root field configs.
|
|
104
|
+
*/ export function nodeDefinitions(fetchById, typeResolver) {
|
|
105
|
+
const nodeInterface = new GraphQLInterfaceType({
|
|
106
|
+
name: 'Node',
|
|
107
|
+
description: 'An object with an ID',
|
|
108
|
+
fields: ()=>({
|
|
109
|
+
id: {
|
|
110
|
+
type: new GraphQLNonNull(GraphQLID),
|
|
111
|
+
description: 'The id of the object.'
|
|
112
|
+
}
|
|
113
|
+
}),
|
|
114
|
+
resolveType: typeResolver
|
|
115
|
+
});
|
|
116
|
+
const nodeField = {
|
|
117
|
+
description: 'Fetches an object given its ID',
|
|
118
|
+
type: nodeInterface,
|
|
119
|
+
args: {
|
|
120
|
+
id: {
|
|
121
|
+
type: new GraphQLNonNull(GraphQLID),
|
|
122
|
+
description: 'The ID of an object'
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
resolve: (_obj, { id }, context, info)=>fetchById(id, context, info)
|
|
126
|
+
};
|
|
127
|
+
const nodesField = {
|
|
128
|
+
description: 'Fetches objects given their IDs',
|
|
129
|
+
type: new GraphQLNonNull(new GraphQLList(nodeInterface)),
|
|
130
|
+
args: {
|
|
131
|
+
ids: {
|
|
132
|
+
type: new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(GraphQLID))),
|
|
133
|
+
description: 'The IDs of objects'
|
|
134
|
+
}
|
|
135
|
+
},
|
|
136
|
+
resolve: (_obj, { ids }, context, info)=>ids.map((id)=>fetchById(id, context, info))
|
|
137
|
+
};
|
|
138
|
+
return {
|
|
139
|
+
nodeInterface,
|
|
140
|
+
nodeField,
|
|
141
|
+
nodesField
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
function isPromiseLike(value) {
|
|
145
|
+
return typeof value?.then === 'function';
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Returns a GraphQLFieldConfig for the mutation described by the provided
|
|
149
|
+
* config, wiring the Relay `clientMutationId` round-trip through a generated
|
|
150
|
+
* `${name}Input`/`${name}Payload` pair.
|
|
151
|
+
*/ export function mutationWithClientMutationId(config) {
|
|
152
|
+
const { name, inputFields, outputFields, mutateAndGetPayload } = config;
|
|
153
|
+
const outputType = new GraphQLObjectType({
|
|
154
|
+
name: `${name}Payload`,
|
|
155
|
+
fields: ()=>({
|
|
156
|
+
...resolveObjMapThunk(outputFields),
|
|
157
|
+
clientMutationId: {
|
|
158
|
+
type: GraphQLString
|
|
159
|
+
}
|
|
160
|
+
})
|
|
161
|
+
});
|
|
162
|
+
const inputType = new GraphQLInputObjectType({
|
|
163
|
+
name: `${name}Input`,
|
|
164
|
+
fields: ()=>({
|
|
165
|
+
...resolveObjMapThunk(inputFields),
|
|
166
|
+
clientMutationId: {
|
|
167
|
+
type: GraphQLString
|
|
168
|
+
}
|
|
169
|
+
})
|
|
170
|
+
});
|
|
171
|
+
return {
|
|
172
|
+
type: outputType,
|
|
173
|
+
description: config.description,
|
|
174
|
+
deprecationReason: config.deprecationReason,
|
|
175
|
+
extensions: config.extensions,
|
|
176
|
+
args: {
|
|
177
|
+
input: {
|
|
178
|
+
type: new GraphQLNonNull(inputType)
|
|
179
|
+
}
|
|
180
|
+
},
|
|
181
|
+
resolve: (_source, { input }, context, info)=>{
|
|
182
|
+
const { clientMutationId } = input;
|
|
183
|
+
const injectClientMutationId = (data)=>{
|
|
184
|
+
if (typeof data === 'object' && data !== null) {
|
|
185
|
+
;
|
|
186
|
+
data.clientMutationId = clientMutationId;
|
|
187
|
+
}
|
|
188
|
+
return data;
|
|
189
|
+
};
|
|
190
|
+
const payload = mutateAndGetPayload(input, context, info);
|
|
191
|
+
if (isPromiseLike(payload)) {
|
|
192
|
+
return payload.then((value)=>injectClientMutationId(value));
|
|
193
|
+
}
|
|
194
|
+
return injectClientMutationId(payload);
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
}
|
package/lib/schema.d.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { AttachmentID, DocumentModelID } from '@kubun/id';
|
|
2
2
|
import type { AggregateHaving, AggregateSpec, ArrayModel, DocumentData, DocumentModel, DocumentModelSchema, DocumentModelsRecord, DocumentNode, GroupedAggregateOrderTerm, ObjectModel, OrderByDirection as OrderByDirectionValue, ReferencesRecordModel, ScalarModel, StringCustomModel, StringEnumModel, StringFormatModel, StringModel, TypeModel } from '@kubun/protocol';
|
|
3
|
-
import { type FieldNode, GraphQLEnumType, type GraphQLFieldConfig, type GraphQLFieldConfigMap, type GraphQLFieldResolver, GraphQLInputObjectType, type GraphQLInputType, GraphQLInterfaceType, GraphQLList, GraphQLNonNull, GraphQLObjectType, type GraphQLOutputType, type GraphQLResolveInfo, type GraphQLScalarType, GraphQLSchema, type SelectionNode } from 'graphql';
|
|
3
|
+
import { type FieldNode, GraphQLEnumType, type GraphQLFieldConfig, type GraphQLFieldConfigMap, type GraphQLFieldResolver, GraphQLInputObjectType, type GraphQLInputType, GraphQLInterfaceType, GraphQLList, GraphQLNonNull, type GraphQLNullableInputType, type GraphQLNullableOutputType, GraphQLObjectType, type GraphQLOutputType, type GraphQLResolveInfo, type GraphQLScalarType, GraphQLSchema, type SelectionNode } from 'graphql';
|
|
4
4
|
import type { Context } from './context.js';
|
|
5
5
|
export declare function toDocumentModelID(id: DocumentModelID | string): DocumentModelID;
|
|
6
6
|
export declare function getGlobalID(id: DocumentModelID | string, globalID: DocumentModelID | string): DocumentModelID;
|
|
7
|
-
export declare function toList(type:
|
|
8
|
-
export declare function toOuputList(type:
|
|
7
|
+
export declare function toList(type: GraphQLNullableInputType): GraphQLList<GraphQLNonNull<GraphQLNullableInputType>>;
|
|
8
|
+
export declare function toOuputList(type: GraphQLNullableOutputType): GraphQLList<GraphQLNonNull<GraphQLNullableOutputType>>;
|
|
9
9
|
export declare const PatchOperation: GraphQLInputObjectType;
|
|
10
10
|
export type GraphQLNodeDefinitions = {
|
|
11
11
|
nodeInterface: GraphQLInterfaceType;
|
|
@@ -99,16 +99,18 @@ export declare class SchemaBuilder {
|
|
|
99
99
|
resolve: (localID: string, ctx: Context) => Promise<unknown> | unknown;
|
|
100
100
|
}>;
|
|
101
101
|
_subscriptions: GraphQLFieldConfigMap<unknown, Context>;
|
|
102
|
-
_types: Record<string,
|
|
102
|
+
_types: Record<string, GraphQLNullableOutputType>;
|
|
103
103
|
constructor(params: SchemaBuilderParams);
|
|
104
104
|
_resolveModelIdentifier(identifier: string): string;
|
|
105
105
|
_resolveModelIDsToInclude(): Set<string>;
|
|
106
106
|
getModelIDs(id: string | null): Array<string>;
|
|
107
107
|
build(): GraphQLSchema;
|
|
108
108
|
_getDefinitions(): SharedDefinitions;
|
|
109
|
-
_getReference(path: string, parentName?: string):
|
|
109
|
+
_getReference(path: string, parentName?: string): GraphQLNullableOutputType;
|
|
110
110
|
_getReferenceSchema(id: string): TypeModel;
|
|
111
|
-
|
|
111
|
+
_getInputObject(key: string): GraphQLInputObjectType;
|
|
112
|
+
_getType(key: string): GraphQLOutputType;
|
|
113
|
+
_getReferenceInput(path: string, isPartial: boolean, parentName?: string): GraphQLNullableInputType;
|
|
112
114
|
_buildSharedDefinitions(): SharedDefinitions;
|
|
113
115
|
_buildDocument(id: string): GraphQLObjectType<DocumentNode, Context> | GraphQLInterfaceType;
|
|
114
116
|
/**
|
|
@@ -189,9 +191,9 @@ export declare class SchemaBuilder {
|
|
|
189
191
|
_buildObjectFilterInput(id: string, schema: DocumentModelSchema | ObjectModel, parentName?: string, isDocument?: boolean): GraphQLInputObjectType;
|
|
190
192
|
_buildObjectOrderByInput(id: string, schema: DocumentModelSchema | ObjectModel, parentName?: string, isDocument?: boolean): GraphQLInputObjectType;
|
|
191
193
|
_buildSetInputObjectType(id: string, withFields: Array<string>, relationField?: string): GraphQLInputObjectType;
|
|
192
|
-
_buildReference(id: string, schema: TypeModel, parentName?: string):
|
|
194
|
+
_buildReference(id: string, schema: TypeModel, parentName?: string): GraphQLNullableOutputType;
|
|
193
195
|
_buildScalar(id: string, schema: ScalarModel, parentName?: string): GraphQLScalarType;
|
|
194
|
-
_buildArray(schema: ArrayModel, parentName?: string): GraphQLList<GraphQLNonNull<
|
|
196
|
+
_buildArray(schema: ArrayModel, parentName?: string): GraphQLList<GraphQLNonNull<GraphQLNullableOutputType>>;
|
|
195
197
|
_buildObject(id: string, schema: ObjectModel, parentName?: string): GraphQLObjectType;
|
|
196
198
|
_buildObjectFields(id: string, schema: DocumentModelSchema | ObjectModel, parentName?: string): GraphQLFieldConfigMap<Record<string, unknown>, Context>;
|
|
197
199
|
_buildNestedAttachmentField(key: string, fieldRef: string): GraphQLFieldConfig<Record<string, unknown>, Context> | null;
|
package/lib/schema.js
CHANGED
|
@@ -2,9 +2,9 @@ import { AttachmentID, DocumentID, DocumentModelID, decodeID, PluginNodeID } fro
|
|
|
2
2
|
import { REFERENCE_PREFIX } from '@kubun/protocol';
|
|
3
3
|
import { pascalCase } from 'change-case';
|
|
4
4
|
import { extendSchema, GraphQLBoolean, GraphQLEnumType, GraphQLFloat, GraphQLID, GraphQLInputObjectType, GraphQLInt, GraphQLInterfaceType, GraphQLList, GraphQLNonNull, GraphQLObjectType, GraphQLSchema, GraphQLString, isEnumType, isListType, isNonNullType, isObjectType, isScalarType, Kind, parse as parseGraphQL } from 'graphql';
|
|
5
|
-
import { connectionArgs, connectionDefinitions, mutationWithClientMutationId, nodeDefinitions } from 'graphql-relay';
|
|
6
5
|
import { GraphQLDateTimeISO, GraphQLDID, GraphQLDuration, GraphQLEmailAddress, GraphQLJSON, GraphQLJSONObject } from 'graphql-scalars';
|
|
7
6
|
import { GraphQLAttachmentID, GraphQLCredentialEntryID, GraphQLDocID } from './identifiers.js';
|
|
7
|
+
import { connectionArgs, connectionDefinitions, mutationWithClientMutationId, nodeDefinitions } from './relay.js';
|
|
8
8
|
import { GraphQLURL } from './scalars.js';
|
|
9
9
|
import { decodeSearchCursor, encodeSearchCursor } from './search-cursor.js';
|
|
10
10
|
import { getUniqueDocValue, validateAccessIdentifiers } from './utils.js';
|
|
@@ -434,7 +434,11 @@ export class SchemaBuilder {
|
|
|
434
434
|
// Extract inverse relations
|
|
435
435
|
for (const [fieldName, meta] of Object.entries(model.fieldsMeta)){
|
|
436
436
|
if (meta.type === 'document' && meta.model != null) {
|
|
437
|
-
const
|
|
437
|
+
const relationRef = model.schema.properties[fieldName]?.$ref;
|
|
438
|
+
if (relationRef == null) {
|
|
439
|
+
throw new Error(`Field ${fieldName} not found on model ${modelIDstring}`);
|
|
440
|
+
}
|
|
441
|
+
const relationRefID = getReferenceID(relationRef);
|
|
438
442
|
const isList = this._getReferenceSchema(relationRefID).type === 'array';
|
|
439
443
|
const relationID = getGlobalID(meta.model, modelID).toString();
|
|
440
444
|
this._relationsTo[relationID] ??= {};
|
|
@@ -537,7 +541,7 @@ export class SchemaBuilder {
|
|
|
537
541
|
this._buildDocument(id);
|
|
538
542
|
const name = this._aliases[id];
|
|
539
543
|
const filterArg = {
|
|
540
|
-
type: this.
|
|
544
|
+
type: this._getInputObject(`${id}-filter`)
|
|
541
545
|
};
|
|
542
546
|
queryFields[`all${name}`] = {
|
|
543
547
|
type: this._types[`${id}-connection`],
|
|
@@ -545,7 +549,7 @@ export class SchemaBuilder {
|
|
|
545
549
|
...connectionArgs,
|
|
546
550
|
filter: filterArg,
|
|
547
551
|
orderBy: {
|
|
548
|
-
type: toList(this.
|
|
552
|
+
type: toList(this._getInputObject(`${id}-order`))
|
|
549
553
|
},
|
|
550
554
|
search: {
|
|
551
555
|
type: GraphQLString
|
|
@@ -557,13 +561,17 @@ export class SchemaBuilder {
|
|
|
557
561
|
};
|
|
558
562
|
}
|
|
559
563
|
// Add top-level search query across all models
|
|
560
|
-
// Get the PageInfo type from an existing connection (shared
|
|
564
|
+
// Get the PageInfo type from an existing connection (shared across all connections)
|
|
561
565
|
const firstModelID = [
|
|
562
566
|
...this._includedModelIDs
|
|
563
567
|
][0];
|
|
564
568
|
if (firstModelID != null) {
|
|
565
569
|
const someConnectionType = this._types[`${firstModelID}-connection`];
|
|
566
|
-
const
|
|
570
|
+
const pageInfoField = someConnectionType.getFields().pageInfo;
|
|
571
|
+
if (pageInfoField == null) {
|
|
572
|
+
throw new Error('Connection type is missing the pageInfo field');
|
|
573
|
+
}
|
|
574
|
+
const pageInfoType = pageInfoField.type;
|
|
567
575
|
const searchResultEdge = new GraphQLObjectType({
|
|
568
576
|
name: 'SearchResultEdge',
|
|
569
577
|
fields: ()=>({
|
|
@@ -641,6 +649,9 @@ export class SchemaBuilder {
|
|
|
641
649
|
const edges = [];
|
|
642
650
|
for(let j = 0; j < page.length; j++){
|
|
643
651
|
const r = page[j];
|
|
652
|
+
if (r == null) {
|
|
653
|
+
continue;
|
|
654
|
+
}
|
|
644
655
|
const node = docMap.get(r.documentID);
|
|
645
656
|
if (node != null) {
|
|
646
657
|
// Cursor = hits consumed through this position, so the next page
|
|
@@ -658,7 +669,7 @@ export class SchemaBuilder {
|
|
|
658
669
|
pageInfo: {
|
|
659
670
|
hasNextPage,
|
|
660
671
|
hasPreviousPage: offset > 0,
|
|
661
|
-
startCursor: edges.length > 0 ? edges[0]
|
|
672
|
+
startCursor: edges.length > 0 ? edges[0]?.cursor ?? null : null,
|
|
662
673
|
endCursor: page.length > 0 ? encodeSearchCursor(endOffset) : null
|
|
663
674
|
}
|
|
664
675
|
};
|
|
@@ -1074,6 +1085,20 @@ export class SchemaBuilder {
|
|
|
1074
1085
|
}
|
|
1075
1086
|
return schema;
|
|
1076
1087
|
}
|
|
1088
|
+
_getInputObject(key) {
|
|
1089
|
+
const input = this._inputObjects[key];
|
|
1090
|
+
if (input == null) {
|
|
1091
|
+
throw new Error(`Could not find input object: ${key}`);
|
|
1092
|
+
}
|
|
1093
|
+
return input;
|
|
1094
|
+
}
|
|
1095
|
+
_getType(key) {
|
|
1096
|
+
const type = this._types[key];
|
|
1097
|
+
if (type == null) {
|
|
1098
|
+
throw new Error(`Could not find type: ${key}`);
|
|
1099
|
+
}
|
|
1100
|
+
return type;
|
|
1101
|
+
}
|
|
1077
1102
|
_getReferenceInput(path, isPartial, parentName = '') {
|
|
1078
1103
|
const id = getReferenceID(path);
|
|
1079
1104
|
const schema = this._getReferenceSchema(id);
|
|
@@ -1176,10 +1201,10 @@ export class SchemaBuilder {
|
|
|
1176
1201
|
args: {
|
|
1177
1202
|
...connectionArgs,
|
|
1178
1203
|
filter: {
|
|
1179
|
-
type: this.
|
|
1204
|
+
type: this._getInputObject(`${id}-filter`)
|
|
1180
1205
|
},
|
|
1181
1206
|
orderBy: {
|
|
1182
|
-
type: toList(this.
|
|
1207
|
+
type: toList(this._getInputObject(`${id}-order`))
|
|
1183
1208
|
}
|
|
1184
1209
|
},
|
|
1185
1210
|
resolve: async (owner, args, ctx)=>{
|
|
@@ -1341,13 +1366,13 @@ export class SchemaBuilder {
|
|
|
1341
1366
|
type: new GraphQLNonNull(toOuputList(groupedAggregateType)),
|
|
1342
1367
|
args: {
|
|
1343
1368
|
groupBy: {
|
|
1344
|
-
type: new GraphQLNonNull(toList(this.
|
|
1369
|
+
type: new GraphQLNonNull(toList(this._getInputObject(`${id}-group-by`)))
|
|
1345
1370
|
},
|
|
1346
1371
|
having: {
|
|
1347
|
-
type: this.
|
|
1372
|
+
type: this._getInputObject(`${id}-having`)
|
|
1348
1373
|
},
|
|
1349
1374
|
orderBy: {
|
|
1350
|
-
type: toList(this.
|
|
1375
|
+
type: toList(this._getInputObject(`${id}-group-order`))
|
|
1351
1376
|
},
|
|
1352
1377
|
limit: {
|
|
1353
1378
|
type: GraphQLInt
|
|
@@ -1396,7 +1421,7 @@ export class SchemaBuilder {
|
|
|
1396
1421
|
name: `Create${name}`,
|
|
1397
1422
|
inputFields: ()=>({
|
|
1398
1423
|
data: {
|
|
1399
|
-
type: new GraphQLNonNull(this.
|
|
1424
|
+
type: new GraphQLNonNull(this._getInputObject(id))
|
|
1400
1425
|
},
|
|
1401
1426
|
transactionID: {
|
|
1402
1427
|
type: GraphQLID
|
|
@@ -1405,7 +1430,7 @@ export class SchemaBuilder {
|
|
|
1405
1430
|
outputFields: ()=>({
|
|
1406
1431
|
...definitions.queryFields,
|
|
1407
1432
|
document: {
|
|
1408
|
-
type: this.
|
|
1433
|
+
type: this._getType(id)
|
|
1409
1434
|
}
|
|
1410
1435
|
}),
|
|
1411
1436
|
mutateAndGetPayload: async (input, ctx, info)=>{
|
|
@@ -1425,7 +1450,7 @@ export class SchemaBuilder {
|
|
|
1425
1450
|
name: `Set${name}`,
|
|
1426
1451
|
inputFields: ()=>({
|
|
1427
1452
|
data: {
|
|
1428
|
-
type: new GraphQLNonNull(this.
|
|
1453
|
+
type: new GraphQLNonNull(this._getInputObject(id))
|
|
1429
1454
|
},
|
|
1430
1455
|
transactionID: {
|
|
1431
1456
|
type: GraphQLID
|
|
@@ -1434,7 +1459,7 @@ export class SchemaBuilder {
|
|
|
1434
1459
|
outputFields: ()=>({
|
|
1435
1460
|
...definitions.queryFields,
|
|
1436
1461
|
document: {
|
|
1437
|
-
type: this.
|
|
1462
|
+
type: this._getType(id)
|
|
1438
1463
|
}
|
|
1439
1464
|
}),
|
|
1440
1465
|
mutateAndGetPayload: async (input, ctx, info)=>{
|
|
@@ -1462,7 +1487,7 @@ export class SchemaBuilder {
|
|
|
1462
1487
|
type: new GraphQLNonNull(toList(PatchOperation))
|
|
1463
1488
|
},
|
|
1464
1489
|
from: {
|
|
1465
|
-
type: this.
|
|
1490
|
+
type: this._getInputObject(`${id}-update`)
|
|
1466
1491
|
},
|
|
1467
1492
|
transactionID: {
|
|
1468
1493
|
type: GraphQLID
|
|
@@ -1471,7 +1496,7 @@ export class SchemaBuilder {
|
|
|
1471
1496
|
outputFields: ()=>({
|
|
1472
1497
|
...definitions.queryFields,
|
|
1473
1498
|
document: {
|
|
1474
|
-
type: this.
|
|
1499
|
+
type: this._getType(id)
|
|
1475
1500
|
}
|
|
1476
1501
|
}),
|
|
1477
1502
|
mutateAndGetPayload: async (input, ctx, info)=>{
|
|
@@ -1737,14 +1762,14 @@ export class SchemaBuilder {
|
|
|
1737
1762
|
this._types[`${id}-grouped-aggregate`] = groupedAggregateType;
|
|
1738
1763
|
// Map a scalar to its existing value-filter input by name (IntValueFilter etc.).
|
|
1739
1764
|
const valueFilterFor = (scalar)=>{
|
|
1740
|
-
return this.
|
|
1765
|
+
return this._getInputObject(`${scalar.name}ValueFilter`);
|
|
1741
1766
|
};
|
|
1742
1767
|
// Having: each op maps its eligible fields to the value filter matching the op's
|
|
1743
1768
|
// scalar. Sum keeps each numeric field's own scalar; average is always Float;
|
|
1744
1769
|
// min/max keep each field's own scalar (Int / Float / String).
|
|
1745
1770
|
const havingFields = {
|
|
1746
1771
|
count: {
|
|
1747
|
-
type: this.
|
|
1772
|
+
type: this._getInputObject('IntValueFilter')
|
|
1748
1773
|
}
|
|
1749
1774
|
};
|
|
1750
1775
|
if (sumType != null && avgType != null) {
|
|
@@ -1755,7 +1780,7 @@ export class SchemaBuilder {
|
|
|
1755
1780
|
type: valueFilterFor(field.type)
|
|
1756
1781
|
};
|
|
1757
1782
|
havingAvgFields[key] = {
|
|
1758
|
-
type: this.
|
|
1783
|
+
type: this._getInputObject('FloatValueFilter')
|
|
1759
1784
|
};
|
|
1760
1785
|
}
|
|
1761
1786
|
const havingSum = new GraphQLInputObjectType({
|
|
@@ -2281,7 +2306,7 @@ export class SchemaBuilder {
|
|
|
2281
2306
|
const scalar = this._buildScalar(id, schema, refParentName);
|
|
2282
2307
|
const knownFilter = VALUE_FILTER_INPUT_TYPES[scalar.name];
|
|
2283
2308
|
if (knownFilter != null) {
|
|
2284
|
-
return this.
|
|
2309
|
+
return this._getInputObject(knownFilter);
|
|
2285
2310
|
}
|
|
2286
2311
|
const filter = isEnumType(scalar) ? createEnumValueFilterInput(scalar) : createScalarValueFilterInput(scalar, {
|
|
2287
2312
|
stringBacked: true
|
|
@@ -2289,7 +2314,7 @@ export class SchemaBuilder {
|
|
|
2289
2314
|
if (this._inputObjects[filter.name] == null) {
|
|
2290
2315
|
this._inputObjects[filter.name] = filter;
|
|
2291
2316
|
}
|
|
2292
|
-
return this.
|
|
2317
|
+
return this._getInputObject(filter.name);
|
|
2293
2318
|
}
|
|
2294
2319
|
_buildReferenceOrderByInput(path, parentName = '') {
|
|
2295
2320
|
const id = getReferenceID(path);
|
|
@@ -2312,7 +2337,7 @@ export class SchemaBuilder {
|
|
|
2312
2337
|
const name = this._aliases[id] ?? pascalCase(parentName + (schema.title ?? ''));
|
|
2313
2338
|
const fields = isDocument ? {
|
|
2314
2339
|
_owner: {
|
|
2315
|
-
type: this.
|
|
2340
|
+
type: this._getInputObject('AccountValueFilter')
|
|
2316
2341
|
}
|
|
2317
2342
|
} : {};
|
|
2318
2343
|
for (const [key, value] of Object.entries(schema.properties)){
|
|
@@ -2335,13 +2360,13 @@ export class SchemaBuilder {
|
|
|
2335
2360
|
type: objectInput
|
|
2336
2361
|
},
|
|
2337
2362
|
and: {
|
|
2338
|
-
type: toList(this.
|
|
2363
|
+
type: toList(this._getInputObject(inputID))
|
|
2339
2364
|
},
|
|
2340
2365
|
or: {
|
|
2341
|
-
type: toList(this.
|
|
2366
|
+
type: toList(this._getInputObject(inputID))
|
|
2342
2367
|
},
|
|
2343
2368
|
not: {
|
|
2344
|
-
type: this.
|
|
2369
|
+
type: this._getInputObject(inputID)
|
|
2345
2370
|
}
|
|
2346
2371
|
})
|
|
2347
2372
|
});
|
|
@@ -2387,6 +2412,9 @@ export class SchemaBuilder {
|
|
|
2387
2412
|
return existing;
|
|
2388
2413
|
}
|
|
2389
2414
|
const model = this._record[id];
|
|
2415
|
+
if (model == null) {
|
|
2416
|
+
throw new Error(`Model ${id} not found`);
|
|
2417
|
+
}
|
|
2390
2418
|
const name = this._aliases[id];
|
|
2391
2419
|
this._inputObjects[inputID] = new GraphQLInputObjectType({
|
|
2392
2420
|
name: `With${relationFieldName}${name}Input`,
|
|
@@ -2504,10 +2532,10 @@ export class SchemaBuilder {
|
|
|
2504
2532
|
args: {
|
|
2505
2533
|
...connectionArgs,
|
|
2506
2534
|
filter: {
|
|
2507
|
-
type: this.
|
|
2535
|
+
type: this._getInputObject(`${relationID}-filter`)
|
|
2508
2536
|
},
|
|
2509
2537
|
orderBy: {
|
|
2510
|
-
type: toList(this.
|
|
2538
|
+
type: toList(this._getInputObject(`${relationID}-order`))
|
|
2511
2539
|
}
|
|
2512
2540
|
},
|
|
2513
2541
|
resolve: (doc, args, ctx)=>{
|
package/lib/types.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kubun/graphql",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.1",
|
|
4
4
|
"keywords": [],
|
|
5
5
|
"license": "see LICENSE.md",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -15,27 +15,26 @@
|
|
|
15
15
|
"LICENSE.md"
|
|
16
16
|
],
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"@kubun/hlc": "^0.
|
|
19
|
-
"@kubun/id": "^0.
|
|
20
|
-
"@kubun/logger": "^0.
|
|
21
|
-
"@kubun/protocol": "^0.
|
|
18
|
+
"@kubun/hlc": "^0.17.0",
|
|
19
|
+
"@kubun/id": "^0.17.0",
|
|
20
|
+
"@kubun/logger": "^0.17.0",
|
|
21
|
+
"@kubun/protocol": "^0.17.0",
|
|
22
22
|
"@sozai/async": "^0.2.1",
|
|
23
23
|
"@sozai/codec": "^0.4.0",
|
|
24
24
|
"@sozai/event": "^0.1.3",
|
|
25
25
|
"@sozai/otel": "^0.3.2",
|
|
26
26
|
"change-case": "^5.4.4",
|
|
27
|
-
"graphql": "^
|
|
28
|
-
"graphql-relay": "^0.10.2",
|
|
27
|
+
"graphql": "^17.0.2",
|
|
29
28
|
"graphql-scalars": "^2.0.0",
|
|
30
29
|
"multiformats": "^14.0.5"
|
|
31
30
|
},
|
|
32
31
|
"devDependencies": {
|
|
33
32
|
"@kokuin/capability": "^0.3.0",
|
|
34
33
|
"@kokuin/token": "^0.5.0",
|
|
35
|
-
"@kubun/db": "^0.
|
|
36
|
-
"@kubun/db-better-sqlite": "^0.
|
|
37
|
-
"@kubun/scalars": "^0.
|
|
38
|
-
"@kubun/store-graph": "^0.
|
|
34
|
+
"@kubun/db": "^0.17.0",
|
|
35
|
+
"@kubun/db-better-sqlite": "^0.17.0",
|
|
36
|
+
"@kubun/scalars": "^0.17.0",
|
|
37
|
+
"@kubun/store-graph": "^0.17.0",
|
|
39
38
|
"@kubun/test-utils": "^0.0.0"
|
|
40
39
|
},
|
|
41
40
|
"publishConfig": {
|
|
@@ -44,8 +43,8 @@
|
|
|
44
43
|
"scripts": {
|
|
45
44
|
"build": "pnpm run build:clean && pnpm run build:js && pnpm run build:types",
|
|
46
45
|
"build:clean": "del lib",
|
|
47
|
-
"build:js": "swc src -d ./lib --config-file ../../node_modules/@kigu/dev/swc.json --strip-leading-paths",
|
|
48
|
-
"build:types": "tsc --emitDeclarationOnly --skipLibCheck",
|
|
46
|
+
"build:js": "del 'lib/**/*.js' && swc src -d ./lib --config-file ../../node_modules/@kigu/dev/swc.json --strip-leading-paths",
|
|
47
|
+
"build:types": "del 'lib/**/*.d.ts' 'lib/**/*.d.ts.map' && tsc --emitDeclarationOnly --skipLibCheck",
|
|
49
48
|
"build:types:ci": "tsc --emitDeclarationOnly --declarationMap false",
|
|
50
49
|
"test": "pnpm run test:types && pnpm run test:unit",
|
|
51
50
|
"test:types": "tsc --noEmit -p tsconfig.test.json",
|