@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 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 'graphql-relay';
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 graphql-relay connection (edges/pageInfo are untouched) with the query
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: GraphQLInputType): GraphQLList<GraphQLNonNull<GraphQLInputType>>;
8
- export declare function toOuputList(type: GraphQLOutputType): GraphQLList<GraphQLNonNull<GraphQLOutputType>>;
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, GraphQLOutputType>;
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): GraphQLOutputType;
109
+ _getReference(path: string, parentName?: string): GraphQLNullableOutputType;
110
110
  _getReferenceSchema(id: string): TypeModel;
111
- _getReferenceInput(path: string, isPartial: boolean, parentName?: string): GraphQLInputType;
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): GraphQLOutputType;
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<GraphQLOutputType>>;
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 relationRefID = getReferenceID(model.schema.properties[fieldName]?.$ref);
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._inputObjects[`${id}-filter`]
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._inputObjects[`${id}-order`])
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 by graphql-relay)
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 pageInfoType = someConnectionType.getFields().pageInfo.type;
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].cursor : null,
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._inputObjects[`${id}-filter`]
1204
+ type: this._getInputObject(`${id}-filter`)
1180
1205
  },
1181
1206
  orderBy: {
1182
- type: toList(this._inputObjects[`${id}-order`])
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._inputObjects[`${id}-group-by`]))
1369
+ type: new GraphQLNonNull(toList(this._getInputObject(`${id}-group-by`)))
1345
1370
  },
1346
1371
  having: {
1347
- type: this._inputObjects[`${id}-having`]
1372
+ type: this._getInputObject(`${id}-having`)
1348
1373
  },
1349
1374
  orderBy: {
1350
- type: toList(this._inputObjects[`${id}-group-order`])
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._inputObjects[id])
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._types[id]
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._inputObjects[id])
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._types[id]
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._inputObjects[`${id}-update`]
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._types[id]
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._inputObjects[`${scalar.name}ValueFilter`];
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._inputObjects.IntValueFilter
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._inputObjects.FloatValueFilter
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._inputObjects[knownFilter];
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._inputObjects[filter.name];
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._inputObjects.AccountValueFilter
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._inputObjects[inputID])
2363
+ type: toList(this._getInputObject(inputID))
2339
2364
  },
2340
2365
  or: {
2341
- type: toList(this._inputObjects[inputID])
2366
+ type: toList(this._getInputObject(inputID))
2342
2367
  },
2343
2368
  not: {
2344
- type: this._inputObjects[inputID]
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._inputObjects[`${relationID}-filter`]
2535
+ type: this._getInputObject(`${relationID}-filter`)
2508
2536
  },
2509
2537
  orderBy: {
2510
- type: toList(this._inputObjects[`${relationID}-order`])
2538
+ type: toList(this._getInputObject(`${relationID}-order`))
2511
2539
  }
2512
2540
  },
2513
2541
  resolve: (doc, args, ctx)=>{
package/lib/types.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { DocumentData, DocumentFilter, DocumentOrderBy } from '@kubun/protocol';
2
- import type { ConnectionArguments } from 'graphql-relay';
2
+ import type { ConnectionArguments } from './relay.js';
3
3
  export type PatchAddOperation = {
4
4
  op: 'add';
5
5
  path: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kubun/graphql",
3
- "version": "0.16.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.16.0",
19
- "@kubun/id": "^0.16.0",
20
- "@kubun/logger": "^0.16.0",
21
- "@kubun/protocol": "^0.16.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": "^16.14.2",
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.16.0",
36
- "@kubun/db-better-sqlite": "^0.16.0",
37
- "@kubun/scalars": "^0.16.0",
38
- "@kubun/store-graph": "^0.16.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",