@contember/client-content 2.1.0-alpha.8 → 2.1.0-beta.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.
Files changed (106) hide show
  1. package/CLAUDE.md +52 -0
  2. package/dist/development/src/ContentClient.cjs +12 -0
  3. package/dist/development/src/ContentClient.cjs.map +1 -1
  4. package/dist/development/src/ContentClient.js +12 -0
  5. package/dist/development/src/ContentClient.js.map +1 -1
  6. package/dist/development/src/ContentQueryBuilder.cjs +26 -15
  7. package/dist/development/src/ContentQueryBuilder.cjs.map +1 -1
  8. package/dist/development/src/ContentQueryBuilder.js +26 -15
  9. package/dist/development/src/ContentQueryBuilder.js.map +1 -1
  10. package/dist/development/src/MutationFailedError.cjs +11 -0
  11. package/dist/development/src/MutationFailedError.cjs.map +1 -0
  12. package/dist/development/src/MutationFailedError.js +11 -0
  13. package/dist/development/src/MutationFailedError.js.map +1 -0
  14. package/dist/development/src/nodes/ContentEntitySelection.cjs +47 -12
  15. package/dist/development/src/nodes/ContentEntitySelection.cjs.map +1 -1
  16. package/dist/development/src/nodes/ContentEntitySelection.js +47 -12
  17. package/dist/development/src/nodes/ContentEntitySelection.js.map +1 -1
  18. package/dist/development/src/nodes/ContentOperation.cjs.map +1 -1
  19. package/dist/development/src/nodes/ContentOperation.js.map +1 -1
  20. package/dist/development/src/utils/ContentOperationSet.cjs.map +1 -1
  21. package/dist/development/src/utils/ContentOperationSet.js.map +1 -1
  22. package/dist/development/src/utils/createListArgs.cjs.map +1 -1
  23. package/dist/development/src/utils/createListArgs.js.map +1 -1
  24. package/dist/development/src/utils/createMutationOperationSet.cjs +8 -6
  25. package/dist/development/src/utils/createMutationOperationSet.cjs.map +1 -1
  26. package/dist/development/src/utils/createMutationOperationSet.js +8 -6
  27. package/dist/development/src/utils/createMutationOperationSet.js.map +1 -1
  28. package/dist/development/src/utils/createQueryOperationSet.cjs.map +1 -1
  29. package/dist/development/src/utils/createQueryOperationSet.js.map +1 -1
  30. package/dist/development/src/utils/createTypedArgs.cjs.map +1 -1
  31. package/dist/development/src/utils/createTypedArgs.js.map +1 -1
  32. package/dist/development/src/utils/mutationFragments.cjs.map +1 -1
  33. package/dist/development/src/utils/mutationFragments.js.map +1 -1
  34. package/dist/production/src/ContentClient.cjs +12 -0
  35. package/dist/production/src/ContentClient.cjs.map +1 -1
  36. package/dist/production/src/ContentClient.js +12 -0
  37. package/dist/production/src/ContentClient.js.map +1 -1
  38. package/dist/production/src/ContentQueryBuilder.cjs +26 -15
  39. package/dist/production/src/ContentQueryBuilder.cjs.map +1 -1
  40. package/dist/production/src/ContentQueryBuilder.js +26 -15
  41. package/dist/production/src/ContentQueryBuilder.js.map +1 -1
  42. package/dist/production/src/MutationFailedError.cjs +11 -0
  43. package/dist/production/src/MutationFailedError.cjs.map +1 -0
  44. package/dist/production/src/MutationFailedError.js +11 -0
  45. package/dist/production/src/MutationFailedError.js.map +1 -0
  46. package/dist/production/src/nodes/ContentEntitySelection.cjs +47 -12
  47. package/dist/production/src/nodes/ContentEntitySelection.cjs.map +1 -1
  48. package/dist/production/src/nodes/ContentEntitySelection.js +47 -12
  49. package/dist/production/src/nodes/ContentEntitySelection.js.map +1 -1
  50. package/dist/production/src/nodes/ContentOperation.cjs.map +1 -1
  51. package/dist/production/src/nodes/ContentOperation.js.map +1 -1
  52. package/dist/production/src/utils/ContentOperationSet.cjs.map +1 -1
  53. package/dist/production/src/utils/ContentOperationSet.js.map +1 -1
  54. package/dist/production/src/utils/createListArgs.cjs.map +1 -1
  55. package/dist/production/src/utils/createListArgs.js.map +1 -1
  56. package/dist/production/src/utils/createMutationOperationSet.cjs +8 -6
  57. package/dist/production/src/utils/createMutationOperationSet.cjs.map +1 -1
  58. package/dist/production/src/utils/createMutationOperationSet.js +8 -6
  59. package/dist/production/src/utils/createMutationOperationSet.js.map +1 -1
  60. package/dist/production/src/utils/createQueryOperationSet.cjs.map +1 -1
  61. package/dist/production/src/utils/createQueryOperationSet.js.map +1 -1
  62. package/dist/production/src/utils/createTypedArgs.cjs.map +1 -1
  63. package/dist/production/src/utils/createTypedArgs.js.map +1 -1
  64. package/dist/production/src/utils/mutationFragments.cjs.map +1 -1
  65. package/dist/production/src/utils/mutationFragments.js.map +1 -1
  66. package/dist/types/ContentClient.d.ts +10 -0
  67. package/dist/types/ContentClient.d.ts.map +1 -1
  68. package/dist/types/ContentQueryBuilder.d.ts.map +1 -1
  69. package/dist/types/MutationFailedError.d.ts +13 -0
  70. package/dist/types/MutationFailedError.d.ts.map +1 -0
  71. package/dist/types/TypedContentQueryBuilder.d.ts.map +1 -1
  72. package/dist/types/nodes/ContentEntitySelection.d.ts +4 -1
  73. package/dist/types/nodes/ContentEntitySelection.d.ts.map +1 -1
  74. package/dist/types/nodes/TypedEntitySelection.d.ts +1 -0
  75. package/dist/types/nodes/TypedEntitySelection.d.ts.map +1 -1
  76. package/dist/types/tsconfig.tsbuildinfo +1 -1
  77. package/dist/types/types/Input.d.ts.map +1 -1
  78. package/dist/types/types/Result.d.ts +30 -9
  79. package/dist/types/types/Result.d.ts.map +1 -1
  80. package/dist/types/utils/createListArgs.d.ts.map +1 -1
  81. package/dist/types/utils/createMutationOperationSet.d.ts.map +1 -1
  82. package/dist/types/utils/createQueryOperationSet.d.ts.map +1 -1
  83. package/package.json +6 -6
  84. package/src/ContentClient.ts +36 -9
  85. package/src/ContentQueryBuilder.ts +45 -35
  86. package/src/MutationFailedError.ts +19 -0
  87. package/src/TypedContentQueryBuilder.ts +0 -4
  88. package/src/nodes/ContentEntitySelection.ts +67 -23
  89. package/src/nodes/TypedEntitySelection.ts +96 -48
  90. package/src/types/Input.ts +1 -9
  91. package/src/types/Result.ts +41 -9
  92. package/src/types/Schema.ts +0 -1
  93. package/src/utils/createListArgs.ts +5 -1
  94. package/src/utils/createMutationOperationSet.ts +9 -7
  95. package/src/utils/createQueryOperationSet.ts +0 -1
  96. package/tests/cases/unit/__snapshots__/mutation.test.ts.snap +952 -818
  97. package/tests/cases/unit/__snapshots__/query.test.ts.snap +216 -60
  98. package/tests/cases/unit/mutation.test.ts +48 -3
  99. package/tests/cases/unit/query.test.ts +103 -82
  100. package/tests/cases/unit/types.test.ts +31 -27
  101. package/tests/client/entities.ts +2 -0
  102. package/tests/client/enums.ts +5 -0
  103. package/tests/client/names.ts +129 -117
  104. package/tests/generate.ts +0 -1
  105. package/tests/lib.ts +0 -1
  106. package/tests/schema.ts +1 -2
@@ -4,10 +4,10 @@ import { GraphQlQueryPrinter } from '@contember/graphql-builder'
4
4
  import { ContentMutation, ContentQuery } from './nodes'
5
5
  import { createMutationOperationSet } from './utils/createMutationOperationSet'
6
6
  import { createQueryOperationSet } from './utils/createQueryOperationSet'
7
+ import { MutationError, ValidationResult } from './types'
8
+ import { MutationFailedError } from './MutationFailedError'
7
9
 
8
-
9
- export type QueryExecutorOptions =
10
- & GraphQlClientRequestOptions
10
+ export type QueryExecutorOptions = GraphQlClientRequestOptions
11
11
 
12
12
  export class ContentClient {
13
13
  constructor(
@@ -15,9 +15,11 @@ export class ContentClient {
15
15
  ) {
16
16
  }
17
17
 
18
-
19
18
  public async query<Value>(query: ContentQuery<Value>, options?: QueryExecutorOptions): Promise<Value>
20
- public async query<Values extends Record<string, any>>(queries: {[K in keyof Values]: ContentQuery<Values[K]>}, options?: QueryExecutorOptions): Promise<Values>
19
+ public async query<Values extends Record<string, any>>(
20
+ queries: { [K in keyof Values]: ContentQuery<Values[K]> },
21
+ options?: QueryExecutorOptions,
22
+ ): Promise<Values>
21
23
  public async query(queries: Record<string, ContentQuery<any>> | ContentQuery<any>, options?: QueryExecutorOptions): Promise<any> {
22
24
  const printer = new GraphQlQueryPrinter()
23
25
  const operationSet = createQueryOperationSet(queries)
@@ -26,10 +28,16 @@ export class ContentClient {
26
28
  return operationSet.parse(result)
27
29
  }
28
30
 
29
- public async mutate<Value>(mutation: ContentMutation<Value>, options?: QueryExecutorOptions,): Promise<Value>
30
- public async mutate<Value>(mutations: ContentMutation<Value>[], options?: QueryExecutorOptions,): Promise<Value[]>
31
- public async mutate<Values extends Record<string, any>>(mutations: { [K in keyof Values]: ContentMutation<Values[K]> | ContentQuery<Values[K]> }, options?: QueryExecutorOptions): Promise<Values>
32
- public async mutate(input: Record<string, ContentMutation<any> | ContentQuery<any>> | ContentMutation<any> | ContentMutation<any>[], options?: QueryExecutorOptions): Promise<any> {
31
+ public async mutate<Value>(mutation: ContentMutation<Value>, options?: QueryExecutorOptions): Promise<Value>
32
+ public async mutate<Value>(mutations: ContentMutation<Value>[], options?: QueryExecutorOptions): Promise<Value[]>
33
+ public async mutate<Values extends Record<string, any>>(
34
+ mutations: { [K in keyof Values]: ContentMutation<Values[K]> | ContentQuery<Values[K]> },
35
+ options?: QueryExecutorOptions,
36
+ ): Promise<Values>
37
+ public async mutate(
38
+ input: Record<string, ContentMutation<any> | ContentQuery<any>> | ContentMutation<any> | ContentMutation<any>[],
39
+ options?: QueryExecutorOptions,
40
+ ): Promise<any> {
33
41
  const operationSet = createMutationOperationSet(input)
34
42
 
35
43
  const printer = new GraphQlQueryPrinter()
@@ -38,4 +46,23 @@ export class ContentClient {
38
46
 
39
47
  return operationSet.parse(result)
40
48
  }
49
+
50
+ public async mutateOrThrow<
51
+ Value extends {
52
+ readonly ok: boolean
53
+ readonly errorMessage: string | null
54
+ readonly errors: MutationError[]
55
+ readonly validation: ValidationResult
56
+ },
57
+ >(mutation: ContentMutation<Value>, options?: QueryExecutorOptions): Promise<Value & { readonly ok: true; readonly errorMessage: null }> {
58
+ const result = await this.mutate(mutation, options)
59
+ if (!result.ok) {
60
+ throw new MutationFailedError(
61
+ result.errorMessage ?? 'Unknown error',
62
+ mutation as ContentMutation<Value & { readonly ok: false; readonly errorMessage: string }>,
63
+ result as Value & { readonly ok: false; readonly errorMessage: string },
64
+ )
65
+ }
66
+ return result as Value & { readonly ok: true; readonly errorMessage: null }
67
+ }
41
68
  }
@@ -1,5 +1,12 @@
1
1
  import { ContentClientInput, MutationResult, SchemaNames, TransactionResult } from './types'
2
- import { ContentEntitySelection, ContentEntitySelectionCallback, ContentEntitySelectionContext, ContentMutation, ContentOperation, ContentQuery } from './nodes'
2
+ import {
3
+ ContentEntitySelection,
4
+ ContentEntitySelectionCallback,
5
+ ContentEntitySelectionContext,
6
+ ContentMutation,
7
+ ContentOperation,
8
+ ContentQuery,
9
+ } from './nodes'
3
10
  import { createListArgs } from './utils/createListArgs'
4
11
  import { createTypedArgs } from './utils/createTypedArgs'
5
12
  import { Input } from '@contember/schema'
@@ -21,7 +28,6 @@ export class ContentQueryBuilder {
21
28
  constructor(private readonly schema: SchemaNames) {
22
29
  }
23
30
 
24
-
25
31
  public fragment(name: string, fieldsCallback?: ContentEntitySelectionCallback): ContentEntitySelection {
26
32
  const context = this.createContext(name)
27
33
 
@@ -32,8 +38,6 @@ export class ContentQueryBuilder {
32
38
  return fieldsCallback(entitySelection)
33
39
  }
34
40
 
35
-
36
-
37
41
  public count(name: string, args: Pick<ContentClientInput.AnyListQueryInput, 'filter'>): ContentQuery<number> {
38
42
  const context = this.createContext(name)
39
43
  const fieldName = `paginate${name}`
@@ -51,7 +55,6 @@ export class ContentQueryBuilder {
51
55
  })
52
56
  }
53
57
 
54
-
55
58
  public list(name: string, args: ContentClientInput.AnyListQueryInput, fields: EntitySelectionOrCallback): ContentQuery<any[]> {
56
59
  const context = this.createContext(name)
57
60
  const fieldName = `list${name}`
@@ -59,17 +62,19 @@ export class ContentQueryBuilder {
59
62
  const selection = this.resolveSelection(fields, context)
60
63
 
61
64
  return new ContentOperation('query', fieldName, typedArgs, selection.selectionSet, it => {
65
+ if (!it) return it
62
66
  const transformFn = selection.transformFn
63
67
  if (transformFn) {
64
- return it.map((el: any) => transformFn(el, {
65
- rootValue: it,
66
- }))
68
+ return it.map((el: any) =>
69
+ transformFn(el, {
70
+ rootValue: it,
71
+ })
72
+ )
67
73
  }
68
74
  return it
69
75
  })
70
76
  }
71
77
 
72
-
73
78
  public get(name: string, args: Input.UniqueQueryInput, fields: EntitySelectionOrCallback): ContentQuery<Record<string, unknown> | null> {
74
79
  const context = this.createContext(name)
75
80
  const fieldName = `get${name}`
@@ -89,9 +94,7 @@ export class ContentQueryBuilder {
89
94
  })
90
95
  }
91
96
 
92
-
93
97
  public create(name: string, args: Input.CreateInput, fields?: EntitySelectionOrCallback): ContentMutation<MutationResult> {
94
-
95
98
  const entity = this.getEntity(name)
96
99
  const typedArgs = createTypedArgs(args, {
97
100
  data: `${entity.name}CreateInput!`,
@@ -101,7 +104,6 @@ export class ContentQueryBuilder {
101
104
  }
102
105
 
103
106
  public update(name: string, args: Input.UpdateInput, fields?: EntitySelectionOrCallback): ContentMutation<MutationResult> {
104
-
105
107
  const entity = this.getEntity(name)
106
108
  const typedArgs = createTypedArgs(args, {
107
109
  data: `${entity.name}UpdateInput!`,
@@ -111,9 +113,7 @@ export class ContentQueryBuilder {
111
113
  return this.createMutationOperation(name, 'update', typedArgs, fields)
112
114
  }
113
115
 
114
-
115
116
  public upsert(name: string, args: Input.UpsertInput, fields?: EntitySelectionOrCallback): ContentMutation<MutationResult> {
116
-
117
117
  const entity = this.getEntity(name)
118
118
  const typedArgs = createTypedArgs(args, {
119
119
  update: `${entity.name}UpdateInput!`,
@@ -124,9 +124,7 @@ export class ContentQueryBuilder {
124
124
  return this.createMutationOperation(name, 'upsert', typedArgs, fields)
125
125
  }
126
126
 
127
-
128
127
  public delete(name: string, args: Input.UniqueQueryInput, fields?: EntitySelectionOrCallback): ContentMutation<MutationResult> {
129
-
130
128
  const entity = this.getEntity(name)
131
129
  const typedArgs = createTypedArgs(args, {
132
130
  by: `${entity.name}UniqueWhere!`,
@@ -135,9 +133,12 @@ export class ContentQueryBuilder {
135
133
  return this.createMutationOperation(name, 'delete', typedArgs, fields)
136
134
  }
137
135
 
138
-
139
- private createMutationOperation(name: string, operation: MutationOperation, args: GraphQlFieldTypedArgs, fields?: EntitySelectionOrCallback): ContentMutation<MutationResult> {
140
-
136
+ private createMutationOperation(
137
+ name: string,
138
+ operation: MutationOperation,
139
+ args: GraphQlFieldTypedArgs,
140
+ fields?: EntitySelectionOrCallback,
141
+ ): ContentMutation<MutationResult> {
141
142
  const context = this.createContext(name)
142
143
  const fieldName = `${operation}${name}`
143
144
  const nodeSelection = fields ? this.resolveSelection(fields, context) : undefined
@@ -149,9 +150,11 @@ export class ContentQueryBuilder {
149
150
  }
150
151
  return {
151
152
  ...it,
152
- node: it.node ? nodeSelection.transformFn(it.node, {
153
- rootValue: it.node,
154
- }) : null,
153
+ node: it.node
154
+ ? nodeSelection.transformFn(it.node, {
155
+ rootValue: it.node,
156
+ })
157
+ : null,
155
158
  }
156
159
  })
157
160
  }
@@ -169,15 +172,21 @@ export class ContentQueryBuilder {
169
172
  ...this.createMutationSelection('transaction'),
170
173
  ...combined.selection,
171
174
  ]
172
- return new ContentOperation<TransactionResult<any>, 'mutation'>('mutation', 'transaction', transactionArgs, items, ({ ok, errorMessage, errors, validation, ...data }) => {
173
- return {
174
- ok,
175
- errorMessage,
176
- errors,
177
- validation,
178
- data: combined.parse(data),
179
- }
180
- })
175
+ return new ContentOperation<TransactionResult<any>, 'mutation'>(
176
+ 'mutation',
177
+ 'transaction',
178
+ transactionArgs,
179
+ items,
180
+ ({ ok, errorMessage, errors, validation, ...data }) => {
181
+ return {
182
+ ok,
183
+ errorMessage,
184
+ errors,
185
+ validation,
186
+ data: combined.parse(data),
187
+ }
188
+ },
189
+ )
181
190
  }
182
191
 
183
192
  private createMutationSelection(operation: MutationOperation, selection?: GraphQlSelectionSet): GraphQlSelectionSet {
@@ -189,9 +198,11 @@ export class ContentQueryBuilder {
189
198
  ]),
190
199
  ]
191
200
  if (operation !== 'delete') {
192
- items.push(new GraphQlField(null, 'validation', {}, [
193
- new GraphQlFragmentSpread('ValidationResult'),
194
- ]))
201
+ items.push(
202
+ new GraphQlField(null, 'validation', {}, [
203
+ new GraphQlFragmentSpread('ValidationResult'),
204
+ ]),
205
+ )
195
206
  }
196
207
  if (selection) {
197
208
  items.push(new GraphQlField(null, 'node', {}, selection))
@@ -199,7 +210,6 @@ export class ContentQueryBuilder {
199
210
  return items
200
211
  }
201
212
 
202
-
203
213
  private resolveSelection(
204
214
  fields: EntitySelectionOrCallback,
205
215
  context: ContentEntitySelectionContext<string>,
@@ -0,0 +1,19 @@
1
+ import { MutationError, ValidationResult } from './types'
2
+ import { ContentMutation } from './nodes'
3
+
4
+ export class MutationFailedError<
5
+ Value extends {
6
+ readonly ok: false
7
+ readonly errorMessage: string
8
+ readonly errors: MutationError[]
9
+ readonly validation: ValidationResult
10
+ },
11
+ > extends Error {
12
+ constructor(
13
+ message: string,
14
+ public readonly request: ContentMutation<Value>,
15
+ public readonly result: Value,
16
+ ) {
17
+ super(message)
18
+ }
19
+ }
@@ -2,14 +2,11 @@ import { ContentClientInput, MutationResult, SchemaTypeLike, TransactionResult }
2
2
  import { ContentMutation, ContentQuery, TypedEntitySelection, TypedEntitySelectionCallback } from './nodes'
3
3
  import { MutationTransactionOptions } from './ContentQueryBuilder'
4
4
 
5
-
6
5
  export type TypedContentEntitySelectionOrCallback<TSchema extends SchemaTypeLike, TEntityName extends keyof TSchema['entities'] & string, TValue> =
7
6
  | TypedEntitySelection<TSchema, TEntityName, TSchema['entities'][TEntityName], TValue>
8
7
  | TypedEntitySelectionCallback<TSchema, TEntityName, TSchema['entities'][TEntityName], TValue>
9
8
 
10
-
11
9
  export interface TypedContentQueryBuilder<TSchema extends SchemaTypeLike> {
12
-
13
10
  fragment<EntityName extends keyof TSchema['entities'] & string>(
14
11
  name: EntityName,
15
12
  ): TypedEntitySelection<TSchema, EntityName, TSchema['entities'][EntityName], {}>
@@ -73,4 +70,3 @@ export interface TypedContentQueryBuilder<TSchema extends SchemaTypeLike> {
73
70
  options?: MutationTransactionOptions,
74
71
  ): ContentMutation<TransactionResult<Values>>
75
72
  }
76
-
@@ -14,8 +14,13 @@ export type ContentEntitySelectionContext<Name extends string> = {
14
14
  export type ContentEntitySelectionCallback = (select: ContentEntitySelection) => ContentEntitySelection
15
15
 
16
16
  export type EntitySelectionCommonInput<Alias extends string | null = string | null> = { as?: Alias }
17
- export type EntitySelectionManyArgs<Alias extends string | null = string | null> = ContentClientInput.AnyListQueryInput & EntitySelectionCommonInput<Alias>
18
- export type EntitySelectionManyByArgs<Alias extends string | null = string | null> = { by: Input.UniqueWhere; filter?: Input.Where } & EntitySelectionCommonInput<Alias>
17
+ export type EntitySelectionManyArgs<Alias extends string | null = string | null> =
18
+ & ContentClientInput.AnyListQueryInput
19
+ & EntitySelectionCommonInput<Alias>
20
+ & { totalCount?: boolean }
21
+ export type EntitySelectionManyByArgs<Alias extends string | null = string | null> =
22
+ & { by: Input.UniqueWhere; filter?: Input.Where }
23
+ & EntitySelectionCommonInput<Alias>
19
24
  export type EntitySelectionOneArgs<Alias extends string | null = string | null> = { filter?: Input.Where } & EntitySelectionCommonInput<Alias>
20
25
  export type EntitySelectionColumnArgs<Alias extends string | null = string | null> = EntitySelectionCommonInput<Alias>
21
26
 
@@ -27,13 +32,11 @@ export type EntitySelectionAnyArgs =
27
32
 
28
33
  export type ContentEntitySelectionOrCallback = ContentEntitySelectionCallback | ContentEntitySelection
29
34
 
30
-
31
35
  export type ContentTransformContext = {
32
36
  rootValue: unknown
33
37
  }
34
38
 
35
39
  export class ContentEntitySelection {
36
-
37
40
  /**
38
41
  * @internal
39
42
  */
@@ -47,14 +50,17 @@ export class ContentEntitySelection {
47
50
  ) {
48
51
  }
49
52
 
50
-
51
53
  $(field: string, args?: EntitySelectionColumnArgs): ContentEntitySelection
52
54
  $(field: string, args: EntitySelectionManyArgs, selection: ContentEntitySelectionOrCallback): ContentEntitySelection
53
55
  $(field: string, args: EntitySelectionManyByArgs, selection: ContentEntitySelectionOrCallback): ContentEntitySelection
54
56
  $(field: string, args: EntitySelectionOneArgs, selection: ContentEntitySelectionOrCallback): ContentEntitySelection
55
57
  $(field: string, selection: ContentEntitySelectionOrCallback): ContentEntitySelection
56
58
 
57
- $(field: string, argsOrSelection?: EntitySelectionAnyArgs | ContentEntitySelectionOrCallback, selectionIn?: ContentEntitySelectionOrCallback): ContentEntitySelection {
59
+ $(
60
+ field: string,
61
+ argsOrSelection?: EntitySelectionAnyArgs | ContentEntitySelectionOrCallback,
62
+ selectionIn?: ContentEntitySelectionOrCallback,
63
+ ): ContentEntitySelection {
58
64
  const [args, selection] = typeof argsOrSelection === 'function' || argsOrSelection instanceof ContentEntitySelection
59
65
  ? [{}, argsOrSelection]
60
66
  : [argsOrSelection ?? {}, selectionIn]
@@ -99,6 +105,19 @@ export class ContentEntitySelection {
99
105
  return new ContentEntitySelection(this.context, nodes)
100
106
  }
101
107
 
108
+ omit(...fields: string[]): ContentEntitySelection {
109
+ return new ContentEntitySelection(
110
+ this.context,
111
+ this.selectionSet.filter(it => {
112
+ if (!(it instanceof GraphQlField)) {
113
+ return true
114
+ }
115
+ const alias = it.alias ?? it.name
116
+ return !fields.includes(alias)
117
+ }),
118
+ )
119
+ }
120
+
102
121
  meta(field: string, flags: ('readable' | 'updatable')[]): ContentEntitySelection {
103
122
  const metaField = this.selectionSet.find((it): it is GraphQlField => it instanceof GraphQlField && it.name === '_meta')
104
123
  const selectionWithoutMeta = metaField ? this.selectionSet.filter(it => it !== metaField) : this.selectionSet
@@ -117,11 +136,14 @@ export class ContentEntitySelection {
117
136
  ])
118
137
  }
119
138
 
120
-
121
139
  transform(transform: (value: any, context: ContentTransformContext) => any): ContentEntitySelection {
122
- return new ContentEntitySelection(this.context, this.selectionSet, !this.transformFn ? transform : (value, ctx) => {
123
- return transform(this.transformFn!(value, ctx), ctx)
124
- })
140
+ return new ContentEntitySelection(
141
+ this.context,
142
+ this.selectionSet,
143
+ !this.transformFn ? transform : (value, ctx) => {
144
+ return transform(this.transformFn!(value, ctx), ctx)
145
+ },
146
+ )
125
147
  }
126
148
 
127
149
  private _column(
@@ -142,14 +164,13 @@ export class ContentEntitySelection {
142
164
  return this.withField(field)
143
165
  }
144
166
 
145
-
146
167
  private _many(
147
168
  name: string,
148
169
  args: EntitySelectionManyArgs,
149
170
  fields:
150
171
  | ContentEntitySelectionCallback
151
172
  | ContentEntitySelection,
152
- ): ContentEntitySelection{
173
+ ): ContentEntitySelection {
153
174
  const alias = args.as ?? name
154
175
  const fieldInfo = this.context.entity.fields[name]
155
176
  if (!fieldInfo) {
@@ -166,20 +187,44 @@ export class ContentEntitySelection {
166
187
  }
167
188
 
168
189
  const entitySelection = typeof fields === 'function' ? fields(new ContentEntitySelection(newContext, [])) : fields
190
+
191
+ // Always use paginateRelation API (relay-like Connection format)
192
+ const paginateFieldName = `paginate${name.charAt(0).toUpperCase()}${name.slice(1)}`
193
+ const paginateSelectionSet: GraphQlSelectionSet = [
194
+ ...(args.totalCount
195
+ ? [
196
+ new GraphQlField(null, 'pageInfo', {}, [
197
+ new GraphQlField(null, 'totalCount'),
198
+ ]),
199
+ ]
200
+ : []),
201
+ new GraphQlField(null, 'edges', {}, [
202
+ new GraphQlField(null, 'node', {}, entitySelection.selectionSet),
203
+ ]),
204
+ ]
169
205
  const newObjectField = new GraphQlField(
170
206
  alias,
171
- name,
172
- createListArgs(entity, args),
173
- entitySelection.selectionSet,
207
+ paginateFieldName,
208
+ createListArgs(entity, args, 'paginate'),
209
+ paginateSelectionSet,
174
210
  )
175
211
  const selectionWithField = this.withField(newObjectField)
212
+ const includeTotalCount = args.totalCount ?? false
176
213
  const nestedTransform = entitySelection.transformFn
177
- if (!nestedTransform) {
178
- return selectionWithField
179
- }
180
- return this.withFieldTransform(alias, it => it.map(nestedTransform))
181
- }
182
214
 
215
+ // Transform: unwrap Connection → flat array, optionally attach totalCount
216
+ return selectionWithField.withFieldTransform(alias, (connection, ctx) => {
217
+ const items = connection.edges.map((edge: any) => nestedTransform ? nestedTransform(edge.node, ctx) : edge.node)
218
+ if (includeTotalCount && connection.pageInfo) {
219
+ Object.defineProperty(items, 'totalCount', {
220
+ value: connection.pageInfo.totalCount,
221
+ enumerable: false,
222
+ writable: false,
223
+ })
224
+ }
225
+ return items
226
+ })
227
+ }
183
228
 
184
229
  private _manyBy(
185
230
  name: string,
@@ -229,7 +274,7 @@ export class ContentEntitySelection {
229
274
  if (!nestedTransform) {
230
275
  return selectionWithField
231
276
  }
232
- return this.withFieldTransform(alias, (it, ctx) => it !== null ? nestedTransform(it, ctx) : null)
277
+ return selectionWithField.withFieldTransform(alias, (it, ctx) => it !== null ? nestedTransform(it, ctx) : null)
233
278
  }
234
279
 
235
280
  private _one(
@@ -273,10 +318,9 @@ export class ContentEntitySelection {
273
318
  if (!nestedTransform) {
274
319
  return selectionWithField
275
320
  }
276
- return this.withFieldTransform(alias, (it, ctx) => it !== null ? nestedTransform(it, ctx) : null)
321
+ return selectionWithField.withFieldTransform(alias, (it, ctx) => it !== null ? nestedTransform(it, ctx) : null)
277
322
  }
278
323
 
279
-
280
324
  private withField(field: GraphQlField) {
281
325
  return new ContentEntitySelection(this.context, [
282
326
  ...this.selectionSet,