graphql 2.6.11 → 2.6.12

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f449cab829c8314cf4cd1b51e225e70603f5dc16dfcb221d3204aa39bd9fe2cd
4
- data.tar.gz: 6fb74ee40392f0663b728c76e72239596fc82271d2222e2d9170d8ad06579f6c
3
+ metadata.gz: 24a0e699fe2f72dd869173b2bc6aa96ec8d40e738a293bc56b9176debca03861
4
+ data.tar.gz: 18b16bf0c467f5f8dc0c00ea2bcf2fbaf53283edc9f09a3789918523090a97ce
5
5
  SHA512:
6
- metadata.gz: 2820887a516c9a015774633ac126924642ceb778b523d0563af9ba766f6a3bfa5fa89ad0420c9ea3ee5208fe8fcdd58101aff1777b44de98c8c12b01fd64be0d
7
- data.tar.gz: 90856cb6b43ac1be362d3358820857c1c8cc038f19def34a119677a6924c7b1441eca628c9e26a2134ae52aaab7507cea3521e36cad2763614909c10d8a79577
6
+ metadata.gz: 74a34839ced3a300d20ca370cb37a7c88c56345ecb056e9b9caa7a424fdb7573c5a014b80a32772cc2a5eb40d83e568c5d0875d93b029b1663dda9547fc06ba1
7
+ data.tar.gz: 65dcfea9802c9fb682e6595ef2d2d224a256c0650ff24fa57774eac420b27e594b3c432a104d76c8e033c659a608940661ea4503e0737f63d7839ff36f5f4405
@@ -254,6 +254,8 @@ module GraphQL
254
254
  task = Async::Task.new do |root_task|
255
255
  run.root_task = root_task
256
256
  root_task.graphql_async_dataloader_run = run
257
+ # Tasks inherit the spawning fiber's storage; give this run its own execution state.
258
+ Fiber[:__graphql_runtime_info] = nil
257
259
  set_fiber_variables(fiber_vars)
258
260
 
259
261
  while first_pass || run.running? || !jobs.empty?
@@ -263,6 +263,8 @@ module GraphQL
263
263
  def spawn_fiber
264
264
  fiber_vars = get_fiber_variables
265
265
  Fiber.new(blocking: !@nonblocking) do
266
+ # Fibers inherit the spawning fiber's storage; give each one its own execution state.
267
+ Fiber[:__graphql_runtime_info] = nil
266
268
  set_fiber_variables(fiber_vars)
267
269
  yield
268
270
  ensure
@@ -2,6 +2,10 @@
2
2
  module GraphQL
3
3
  module Execution
4
4
  class FieldResolveStep
5
+ # Only these built-in fields on Relay connection and edge types return already-scoped items.
6
+ # Custom fields on those types must still be authorized normally.
7
+ SCOPED_RELAY_FIELD_NAMES = [:node, :nodes, :edges].freeze
8
+
5
9
  def initialize(parent_type:, runner:, key:, selections_step:)
6
10
  @selections_step = selections_step
7
11
  @key = key
@@ -283,7 +287,7 @@ module GraphQL
283
287
  authorized_objects = objects
284
288
  end
285
289
 
286
- if @parent_type.default_relay? && authorized_objects.all? { |o| o.respond_to?(:was_authorized_by_scope_items?) && o.was_authorized_by_scope_items? }
290
+ if @parent_type.default_relay? && SCOPED_RELAY_FIELD_NAMES.include?(@field_definition.original_name) && authorized_objects.all? { |o| o.respond_to?(:was_authorized_by_scope_items?) && o.was_authorized_by_scope_items? }
287
291
  @was_scoped = true
288
292
  end
289
293
 
@@ -527,7 +531,7 @@ module GraphQL
527
531
  end
528
532
 
529
533
  inner_type = return_type.of_type
530
- result_a = Array.new(field_result.size)
534
+ result_a = field_result.is_a?(Array) ? Array.new(field_result.size) : []
531
535
  field_result.each_with_index do |item, idx|
532
536
  result_a[idx] = build_leaf_result(result_a, idx, item, inner_type, ctx, true)
533
537
  end
@@ -645,11 +649,18 @@ module GraphQL
645
649
  list_result = graphql_result[key] = []
646
650
  @directive_finalizers&.each { |f| @runner.add_finalizer(@selections_step.query, list_result, nil, f) }
647
651
  i = 0
648
- s = field_result.size
649
- while i < s
650
- inner_f_r = field_result[i]
651
- build_graphql_result(list_result, i, inner_f_r, inner_type, inner_type_nn, inner_type_l, true)
652
- i += 1
652
+ if field_result.is_a?(Array)
653
+ s = field_result.size
654
+ while i < s
655
+ inner_f_r = field_result[i]
656
+ build_graphql_result(list_result, i, inner_f_r, inner_type, inner_type_nn, inner_type_l, true)
657
+ i += 1
658
+ end
659
+ else
660
+ field_result.each do |inner_f_r|
661
+ build_graphql_result(list_result, i, inner_f_r, inner_type, inner_type_nn, inner_type_l, true)
662
+ i += 1
663
+ end
653
664
  end
654
665
  elsif @runner.resolves_lazies || (
655
666
  @static_type.kind.object? ?
@@ -619,7 +619,9 @@ module GraphQL
619
619
  when Array
620
620
  # It's an array full of execution errors; add them all.
621
621
  if !value.empty? && value.all?(GraphQL::ExecutionError)
622
- list_type_at_all = (field && (field.type.list?))
622
+ list_type = field && field.type
623
+ list_type = list_type.of_type if list_type && list_type.non_null?
624
+ list_type_at_all = list_type && list_type.list?
623
625
  if selection_result.nil? || !selection_result.graphql_dead
624
626
  value.each_with_index do |error, index|
625
627
  error.ast_node ||= ast_node
@@ -627,7 +629,7 @@ module GraphQL
627
629
  context.errors << error
628
630
  end
629
631
  if selection_result
630
- if list_type_at_all
632
+ if list_type_at_all && !list_type.of_type.non_null?
631
633
  result_without_errors = value.map { |v| v.is_a?(GraphQL::ExecutionError) ? nil : v }
632
634
  set_result(selection_result, result_name, result_without_errors, false, is_non_null)
633
635
  else
@@ -798,6 +800,8 @@ module GraphQL
798
800
 
799
801
  def resolve_list_item(inner_value, inner_type, inner_type_non_null, ast_node, field, owner_object, arguments, this_idx, response_list, owner_type, was_scoped, runtime_state) # rubocop:disable Metrics/ParameterLists
800
802
  runtime_state ||= get_current_runtime_state
803
+ runtime_state.current_field = field
804
+ runtime_state.current_arguments = arguments
801
805
  runtime_state.current_result_name = this_idx
802
806
  runtime_state.current_result = response_list
803
807
  call_method_on_directives(:resolve_each, owner_object, ast_node.directives) do
@@ -65,24 +65,25 @@ module GraphQL
65
65
  end
66
66
 
67
67
  def column_at(pos)
68
- next_line_idx = lines_at.bsearch_index { |l| l >= pos } || 0
69
- if next_line_idx > 0
70
- line_pos = @lines_at[next_line_idx - 1]
71
- pos - line_pos
68
+ next_line_idx = lines_at.bsearch_index { |l| l >= pos } || @lines_at.size
69
+ line_start = next_line_idx == 0 ? 0 : @lines_at[next_line_idx - 1] + 1
70
+ if @graphql_str.ascii_only?
71
+ pos - line_start + 1
72
72
  else
73
- pos + 1
73
+ @graphql_str.byteslice(line_start, pos - line_start).force_encoding(Encoding::UTF_8).length + 1
74
74
  end
75
75
  end
76
76
 
77
77
  private
78
78
 
79
- # @return [Array<Integer>] Positions of each line break in the original string
79
+ # @return [Array<Integer>] Byte offsets of each line break in the original string
80
80
  def lines_at
81
81
  @lines_at ||= begin
82
+ graphql_str = @graphql_str.b
82
83
  la = []
83
84
  idx = 0
84
85
  while idx
85
- idx = @graphql_str.index("\n", idx)
86
+ idx = graphql_str.index("\n", idx)
86
87
  if idx
87
88
  la << idx
88
89
  idx += 1
@@ -88,7 +88,11 @@ module GraphQL
88
88
 
89
89
  # @return [Integer]
90
90
  def offset_from_cursor(cursor)
91
- decode(cursor).to_i
91
+ offset = Integer(decode(cursor), 10, exception: false)
92
+ if offset.nil? || offset <= 0
93
+ raise GraphQL::ExecutionError, "Invalid cursor: #{cursor.inspect}"
94
+ end
95
+ offset
92
96
  end
93
97
 
94
98
  # Abstract this operation so we can always ignore inputs less than zero.
@@ -230,7 +234,7 @@ module GraphQL
230
234
  end
231
235
 
232
236
  def async_dataloader?
233
- @context&.[](:dataloader).is_a?(GraphQL::Dataloader::AsyncDataloader)
237
+ @context&.dataloader.is_a?(GraphQL::Dataloader::AsyncDataloader)
234
238
  end
235
239
 
236
240
  # Load nodes after applying first/last/before/after,
@@ -35,7 +35,11 @@ module GraphQL
35
35
  value_was_provided = @provided_variables.key?(variable_name)
36
36
  max_errors = schema.validate_max_errors - @errors.count if schema.validate_max_errors
37
37
  begin
38
- validation_result = variable_type.validate_input(provided_value, ctx, max_errors: max_errors)
38
+ validation_result = if value_was_provided || default_value.nil?
39
+ variable_type.validate_input(provided_value, ctx, max_errors: max_errors)
40
+ else
41
+ GraphQL::Query::InputValidationResult::VALID
42
+ end
39
43
  if validation_result.valid?
40
44
  if value_was_provided
41
45
  # Add the variable if a value was provided
@@ -33,30 +33,49 @@ module GraphQL
33
33
 
34
34
  def freeze
35
35
  @cached_visible.default_proc = nil
36
+ @cached_visible.freeze
36
37
  @cached_visible_fields.default_proc = nil
37
38
  @cached_visible_fields.each do |type, fields|
38
39
  fields.default_proc = nil
40
+ fields.freeze
39
41
  end
42
+ @cached_visible_fields.freeze
40
43
  @cached_visible_arguments.default_proc = nil
41
44
  @cached_visible_arguments.each do |type, fields|
42
45
  fields.default_proc = nil
46
+ fields.freeze
43
47
  end
48
+ @cached_visible_arguments.freeze
44
49
  @cached_parent_fields.default_proc = nil
45
50
  @cached_parent_fields.each do |type, fields|
46
51
  fields.default_proc = nil
52
+ fields.freeze
47
53
  end
54
+ @cached_parent_fields.freeze
48
55
  @cached_parent_arguments.default_proc = nil
49
56
  @cached_parent_arguments.each do |type, args|
50
57
  args.default_proc = nil
58
+ args.freeze
51
59
  end
60
+ @cached_parent_arguments.freeze
52
61
  @cached_possible_types.default_proc = nil
62
+ @cached_possible_types.freeze
53
63
  @cached_enum_values.default_proc = nil
64
+ @cached_enum_values.freeze
54
65
  @cached_fields.default_proc = nil
66
+ @cached_fields.freeze
55
67
  @cached_arguments.default_proc = nil
68
+ @cached_arguments.freeze
56
69
  @loadable_possible_types.default_proc = nil
70
+ @loadable_possible_types.freeze
57
71
  @cached_field_result.default_proc = nil
58
- @cached_field_result.each { |_, h| h.default_proc = nil }
72
+ @cached_field_result.each do |_, h|
73
+ h.default_proc = nil
74
+ h.freeze
75
+ end
76
+ @cached_field_result.freeze
59
77
  @cached_type_result.default_proc = nil
78
+ @cached_type_result.freeze
60
79
  super
61
80
  end
62
81
 
@@ -277,10 +296,18 @@ module GraphQL
277
296
  arguments(f).each do |arg|
278
297
  argument(f, arg.graphql_name)
279
298
  end
299
+
300
+ resolver_class = f.resolver
301
+ if resolver_class
302
+ arguments(resolver_class).each do |arg|
303
+ argument(resolver_class, arg.graphql_name)
304
+ end
305
+ end
280
306
  end
281
307
  @schema.introspection_system.dynamic_fields.each do |f|
282
308
  field(type_defn, f.graphql_name)
283
309
  end
310
+ interfaces(type_defn)
284
311
  elsif type_defn.kind.input_object?
285
312
  arguments(type_defn).each do |arg|
286
313
  argument(type_defn, arg.graphql_name)
@@ -289,8 +316,9 @@ module GraphQL
289
316
  enum_values(type_defn)
290
317
  elsif type_defn.kind.union?
291
318
  loadable_possible_types(type_defn, @context)
292
- elsif type_defn.kind.object? || type_defn.kind.interface?
293
- interfaces(type_defn)
319
+ @schema.introspection_system.dynamic_fields.each do |f|
320
+ field(type_defn, f.graphql_name)
321
+ end
294
322
  end
295
323
  possible_types(type_defn)
296
324
  end
@@ -313,6 +341,20 @@ module GraphQL
313
341
  argument(directive, directive_argument.graphql_name)
314
342
  end
315
343
  end
344
+
345
+ @cached_arguments.each do |owner, args|
346
+ @cached_parent_arguments[owner]
347
+
348
+ args.each do |arg|
349
+ if (loads_type = arg.loads)
350
+ loadable?(loads_type, @context)
351
+ possible_types(loads_type)
352
+ if loads_type.kind.union?
353
+ loadable_possible_types(loads_type, @context)
354
+ end
355
+ end
356
+ end
357
+ end
316
358
  end
317
359
 
318
360
  private
@@ -16,6 +16,7 @@ module GraphQL
16
16
  @schema = context.schema
17
17
  @inline_fragment_paths = {}
18
18
  @field_unwrapped_types = {}.compare_by_identity
19
+ @child_positions = {}.compare_by_identity
19
20
  super(document)
20
21
  end
21
22
 
@@ -159,6 +160,7 @@ module GraphQL
159
160
  @current_argument_definition = @parent_argument_definition
160
161
  @parent_argument_definition = prev_parent
161
162
  @path_depth -= 1
163
+ @child_positions.delete(node)
162
164
  end
163
165
 
164
166
  def on_fragment_spread(node, parent)
@@ -171,7 +173,7 @@ module GraphQL
171
173
  def on_input_object(node, parent)
172
174
  arg_defn = @current_argument_definition
173
175
  if arg_defn && arg_defn.type.list?
174
- @path[@path_depth] = parent.children.index(node)
176
+ @path[@path_depth] = child_positions(parent)[node]
175
177
  @path_depth += 1
176
178
  super
177
179
  @path_depth -= 1
@@ -180,6 +182,14 @@ module GraphQL
180
182
  end
181
183
  end
182
184
 
185
+ def child_positions(parent)
186
+ @child_positions[parent] ||= begin
187
+ positions = {}.compare_by_identity
188
+ parent.children.each_with_index { |child, index| positions[child] ||= index }
189
+ positions
190
+ end
191
+ end
192
+
183
193
  # @return [GraphQL::BaseType] The current object type
184
194
  def type_definition
185
195
  @current_object_type
@@ -9,18 +9,53 @@ module GraphQL
9
9
  'Schema is not configured for subscriptions',
10
10
  nodes: node
11
11
  ))
12
- elsif node.selections.size != 1
13
- add_error(GraphQL::StaticValidation::NotSingleSubscriptionError.new(
14
- 'A subscription operation may only have one selection',
15
- nodes: node,
16
- ))
17
12
  else
13
+ root_fields = subscription_root_fields(node)
14
+ if root_fields.map { |field| field.alias || field.name }.uniq.size != 1
15
+ add_error(GraphQL::StaticValidation::NotSingleSubscriptionError.new(
16
+ 'A subscription operation may only have one selection',
17
+ nodes: node,
18
+ ))
19
+ elsif root_fields.any? { |field| field.name.start_with?("__") }
20
+ add_error(GraphQL::StaticValidation::NotSingleSubscriptionError.new(
21
+ 'A subscription operation may not select an introspection field',
22
+ nodes: node,
23
+ ))
24
+ end
18
25
  super
19
26
  end
20
27
  else
21
28
  super
22
29
  end
23
30
  end
31
+
32
+ private
33
+
34
+ def subscription_root_fields(node)
35
+ root_type = context.types.subscription_root
36
+ fields = []
37
+ selections = node.selections.dup
38
+ visited_fragments = {}
39
+
40
+ while (selection = selections.pop)
41
+ case selection
42
+ when GraphQL::Language::Nodes::Field
43
+ fields << selection
44
+ when GraphQL::Language::Nodes::FragmentSpread
45
+ next if visited_fragments[selection.name]
46
+ visited_fragments[selection.name] = true
47
+ fragment = context.fragments[selection.name]
48
+ selections << fragment if fragment
49
+ when GraphQL::Language::Nodes::InlineFragment, GraphQL::Language::Nodes::FragmentDefinition
50
+ fragment_type = selection.type ? @types.type(selection.type.name) : root_type
51
+ if fragment_type && fragment_type.kind.composite? && @types.possible_types(fragment_type).include?(root_type)
52
+ selections.concat(selection.selections)
53
+ end
54
+ end
55
+ end
56
+
57
+ fields
58
+ end
24
59
  end
25
60
  end
26
61
  end
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module GraphQL
3
- VERSION = "2.6.11"
3
+ VERSION = "2.6.12"
4
4
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: graphql
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.6.11
4
+ version: 2.6.12
5
5
  platform: ruby
6
6
  authors:
7
7
  - Robert Mosolgo
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2026-09-21 00:00:00.000000000 Z
10
+ date: 2026-10-14 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: base64