graphiti 1.11.1 → 1.12.0
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 +4 -4
- data/.github/workflows/ci.yml +1 -0
- data/CHANGELOG.md +13 -0
- data/CONTRIBUTING.md +77 -0
- data/graphiti.gemspec +18 -0
- data/lib/graphiti/hash_renderer.rb +4 -4
- data/lib/graphiti/query.rb +6 -3
- data/lib/graphiti/request_validators/validator.rb +16 -14
- data/lib/graphiti/resource.rb +26 -0
- data/lib/graphiti/schema.rb +4 -0
- data/lib/graphiti/schema_diff.rb +4 -0
- data/lib/graphiti/serializer.rb +3 -0
- data/lib/graphiti/sideload.rb +42 -8
- data/lib/graphiti/util/attribute_check.rb +31 -4
- data/lib/graphiti/util/serializer_relationships.rb +3 -3
- data/lib/graphiti/version.rb +1 -1
- data/lib/graphiti.rb +12 -0
- data/package.json +47 -10
- metadata +11 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0ffd673e5dc4ec50d5b5aeb01abcb192894c6451cd9bfd54eca081f76bdf1d49
|
|
4
|
+
data.tar.gz: 45ed5cf75d703bd9b573015cd7f798cdc8fe1d5606d198bbdbda09d8e9dd7383
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1472ab72a4ba8982a06a99d4959460ddae1e350ac6f1ce579dbe694c9c2cac6901e46d74b61532895f248e29848361aa311f39320201cb94f002874d64b34386
|
|
7
|
+
data.tar.gz: 44d3d9fbb1f78c6ba6a1fe005dc19a9f64a7b15236075b224571d585754928352d032a5acccd7f6165695990ff5a66750868a13ebd87fec9d33e9446e599fb97
|
data/.github/workflows/ci.yml
CHANGED
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
graphiti changelog
|
|
2
2
|
|
|
3
|
+
# [1.12.0](https://github.com/graphiti-api/graphiti/compare/v1.11.1...v1.12.0) (2026-07-29)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* GQL name chaining ([#415](https://github.com/graphiti-api/graphiti/issues/415)) [skip ci] ([930928a](https://github.com/graphiti-api/graphiti/commit/930928a8779037efbcc1989eef5cf579c608e431))
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* conditional relationships evaluate readable/writable guards per-request ([#450](https://github.com/graphiti-api/graphiti/issues/450)) ([3ed3187](https://github.com/graphiti-api/graphiti/commit/3ed3187068d268cd85ee1c36dc8135aa92abd05c))
|
|
14
|
+
* pass the model and attribute name to writable guards ([#511](https://github.com/graphiti-api/graphiti/issues/511)) [skip ci] ([24a8a94](https://github.com/graphiti-api/graphiti/commit/24a8a94f801867f57cc7c1ae9c6a9bdbfec05c24))
|
|
15
|
+
|
|
3
16
|
## [1.11.1](https://github.com/graphiti-api/graphiti/compare/v1.11.0...v1.11.1) (2026-07-28)
|
|
4
17
|
|
|
5
18
|
# [1.11.0](https://github.com/graphiti-api/graphiti/compare/v1.10.3...v1.11.0) (2026-07-28)
|
data/CONTRIBUTING.md
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
## Commit messages
|
|
4
|
+
|
|
5
|
+
Releases are cut automatically by [semantic-release](https://github.com/semantic-release/semantic-release)
|
|
6
|
+
from commit messages, so the prefix you use decides whether a release happens
|
|
7
|
+
and how the version changes.
|
|
8
|
+
|
|
9
|
+
Format: `type: subject`, or `type(scope): subject`.
|
|
10
|
+
|
|
11
|
+
### Types that release
|
|
12
|
+
|
|
13
|
+
| Prefix | Version bump | Use for |
|
|
14
|
+
| ------ | ------------ | ------- |
|
|
15
|
+
| `fix:` | patch (`1.2.3` → `1.2.4`) | A bug fix users would notice |
|
|
16
|
+
| `perf:` | patch | A performance improvement |
|
|
17
|
+
| `feat:` | minor (`1.2.3` → `1.3.0`) | New functionality |
|
|
18
|
+
|
|
19
|
+
### Types that do not release
|
|
20
|
+
|
|
21
|
+
`build:` `chore:` `ci:` `docs:` `refactor:` `style:` `test:`
|
|
22
|
+
|
|
23
|
+
These land on `main` without publishing a gem. Use them for anything with no
|
|
24
|
+
user-facing change — test fixes, CI tweaks, internal refactors. They are still
|
|
25
|
+
included in the next release's notes.
|
|
26
|
+
|
|
27
|
+
### Breaking changes
|
|
28
|
+
|
|
29
|
+
Either add `!` after the type, or a `BREAKING CHANGE:` footer. Both produce a
|
|
30
|
+
major bump (`1.2.3` → `2.0.0`):
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
feat!: remove deprecated sideload API
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
feat: replace sideload API
|
|
38
|
+
|
|
39
|
+
BREAKING CHANGE: `allow_sideload` no longer accepts a :scope option.
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
The `!` works with a scope too: `feat(adapters)!: ...`
|
|
43
|
+
|
|
44
|
+
Describe what breaks and what to do instead — the footer text is published in
|
|
45
|
+
the release notes verbatim.
|
|
46
|
+
|
|
47
|
+
### Notes
|
|
48
|
+
|
|
49
|
+
- Only the **commit** prefix matters, not the PR title, unless the PR is
|
|
50
|
+
squash-merged with the title as the commit subject.
|
|
51
|
+
- The prefix must be lowercase and followed by `: ` — `Fix: thing` and
|
|
52
|
+
`fix:thing` are both unparsed, and an unparsed commit never triggers a
|
|
53
|
+
release.
|
|
54
|
+
- If nothing since the last tag is `fix:`, `perf:`, `feat:`, or breaking, no
|
|
55
|
+
release is cut. That is expected.
|
|
56
|
+
|
|
57
|
+
## Running the tests
|
|
58
|
+
|
|
59
|
+
```sh
|
|
60
|
+
bundle exec rspec
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
The Rails integration specs are gated behind an environment variable and are
|
|
64
|
+
**silently skipped** without it — a plain `rspec` run reports success while
|
|
65
|
+
covering none of the ActiveRecord adapter:
|
|
66
|
+
|
|
67
|
+
```sh
|
|
68
|
+
BUNDLE_GEMFILE=gemfiles/rails_8_0_graphiti_rails.gemfile \
|
|
69
|
+
APPRAISAL_INITIALIZED=1 \
|
|
70
|
+
bundle exec rspec spec/integration/rails/
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Lint with:
|
|
74
|
+
|
|
75
|
+
```sh
|
|
76
|
+
bundle exec standardrb
|
|
77
|
+
```
|
data/graphiti.gemspec
CHANGED
|
@@ -18,6 +18,24 @@ Gem::Specification.new do |spec|
|
|
|
18
18
|
spec.require_paths = ["lib"]
|
|
19
19
|
spec.required_ruby_version = ">= 2.7"
|
|
20
20
|
|
|
21
|
+
# TODO: remove once the 1.12 upgrade window has passed
|
|
22
|
+
spec.post_install_message = <<~MSG
|
|
23
|
+
Graphiti: relationship readable:/writable: guards are now enforced.
|
|
24
|
+
|
|
25
|
+
Symbols, strings, and procs passed to a relationship's readable/writable
|
|
26
|
+
guards were always interpreted as true and never actually called before this
|
|
27
|
+
conditional relationship change. They are now evaluated per-request, where
|
|
28
|
+
unreadable relationships are omitted from responses and includes, and unwritable
|
|
29
|
+
relationships reject writes.
|
|
30
|
+
|
|
31
|
+
To list every affected relationship in your app:
|
|
32
|
+
|
|
33
|
+
bin/rails runner 'puts Graphiti.guarded_relationships'
|
|
34
|
+
|
|
35
|
+
Audit these before deploying. Apps using schema.json will also see affected
|
|
36
|
+
relationships flagged as "became guarded" by the schema check.
|
|
37
|
+
MSG
|
|
38
|
+
|
|
21
39
|
spec.add_dependency "jsonapi-serializable", "~> 0.3.0"
|
|
22
40
|
spec.add_dependency "jsonapi-renderer", "~> 0.2", ">= 0.2.2"
|
|
23
41
|
spec.add_dependency "dry-types", ">= 0.15.0", "< 2.0"
|
|
@@ -70,13 +70,13 @@ module Graphiti
|
|
|
70
70
|
|
|
71
71
|
serializers = v.send(:resources)
|
|
72
72
|
name = graphql ? k.to_s.camelize(:lower) : k
|
|
73
|
-
|
|
74
|
-
|
|
73
|
+
subchain = name_chain.dup
|
|
74
|
+
subchain << k unless subchain.last == k
|
|
75
75
|
|
|
76
76
|
unless remote_resource? && serializers.nil?
|
|
77
77
|
payload = if serializers.is_a?(Array)
|
|
78
78
|
data = serializers.map { |rr|
|
|
79
|
-
rr.to_hash(fields: fields, include: nested_include, graphql: graphql, name_chain:
|
|
79
|
+
rr.to_hash(fields: fields, include: nested_include, graphql: graphql, name_chain: subchain)
|
|
80
80
|
}
|
|
81
81
|
graphql ? {nodes: data} : data
|
|
82
82
|
elsif serializers.nil?
|
|
@@ -86,7 +86,7 @@ module Graphiti
|
|
|
86
86
|
end
|
|
87
87
|
end
|
|
88
88
|
else
|
|
89
|
-
serializers.to_hash(fields: fields, include: nested_include, graphql: graphql, name_chain:
|
|
89
|
+
serializers.to_hash(fields: fields, include: nested_include, graphql: graphql, name_chain: subchain)
|
|
90
90
|
end
|
|
91
91
|
|
|
92
92
|
attrs[name.to_sym] = payload
|
data/lib/graphiti/query.rb
CHANGED
|
@@ -211,10 +211,13 @@ module Graphiti
|
|
|
211
211
|
allowlist = allowlist[@resource.context_namespace] if allowlist
|
|
212
212
|
end
|
|
213
213
|
|
|
214
|
-
allowlist ? Util::IncludeParams.scrub(requested, allowlist) : requested
|
|
215
|
-
end
|
|
214
|
+
scrubbed = allowlist ? Util::IncludeParams.scrub(requested, allowlist) : requested
|
|
216
215
|
|
|
217
|
-
|
|
216
|
+
scrubbed.filter do |key, value|
|
|
217
|
+
sideload = @resource.class.sideload(key)
|
|
218
|
+
sideload.nil? ? true : sideload.readable?
|
|
219
|
+
end
|
|
220
|
+
end
|
|
218
221
|
end
|
|
219
222
|
|
|
220
223
|
def stats
|
|
@@ -83,20 +83,22 @@ module Graphiti
|
|
|
83
83
|
end
|
|
84
84
|
|
|
85
85
|
def typecast_attributes(resource, attributes, action, payload_path)
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
86
|
+
resource.with_guarded_write(action, attributes[:id]) do
|
|
87
|
+
attributes.each_pair do |key, value|
|
|
88
|
+
# Only validate id if create action, otherwise it's only used for lookup
|
|
89
|
+
next if action != :create &&
|
|
90
|
+
key == :id &&
|
|
91
|
+
resource.class.config[:attributes][:id][:writable] == false
|
|
92
|
+
|
|
93
|
+
begin
|
|
94
|
+
attributes[key] = resource.typecast(key, value, :writable)
|
|
95
|
+
rescue Graphiti::Errors::UnknownAttribute
|
|
96
|
+
@errors.add(fully_qualified_key(key, payload_path), :unknown_attribute, message: "is an unknown attribute")
|
|
97
|
+
rescue Graphiti::Errors::InvalidAttributeAccess
|
|
98
|
+
@errors.add(fully_qualified_key(key, payload_path), :unwritable_attribute, message: "cannot be written")
|
|
99
|
+
rescue Graphiti::Errors::TypecastFailed => e
|
|
100
|
+
@errors.add(fully_qualified_key(key, payload_path), :type_error, message: "should be type #{e.type_name}")
|
|
101
|
+
end
|
|
100
102
|
end
|
|
101
103
|
end
|
|
102
104
|
end
|
data/lib/graphiti/resource.rb
CHANGED
|
@@ -67,6 +67,32 @@ module Graphiti
|
|
|
67
67
|
adapter.base_scope(model)
|
|
68
68
|
end
|
|
69
69
|
|
|
70
|
+
# Exposes write context to attribute guards for the duration of the block.
|
|
71
|
+
# @api private
|
|
72
|
+
def with_guarded_write(action, id)
|
|
73
|
+
@guarded_write = {action: action, id: id}
|
|
74
|
+
yield
|
|
75
|
+
ensure
|
|
76
|
+
remove_instance_variable(:@guarded_write) if defined?(@guarded_write)
|
|
77
|
+
remove_instance_variable(:@guard_model) if defined?(@guard_model)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# The model a writable guard is being asked about. Resolved lazily and
|
|
81
|
+
# memoized, so it costs nothing unless a guard asks for it, and is only
|
|
82
|
+
# resolved once per payload. On create, this is a new unsaved instance.
|
|
83
|
+
# @api private
|
|
84
|
+
def guard_model
|
|
85
|
+
return @guard_model if defined?(@guard_model)
|
|
86
|
+
|
|
87
|
+
@guard_model = if !defined?(@guarded_write)
|
|
88
|
+
nil
|
|
89
|
+
elsif @guarded_write[:action] == :create || @guarded_write[:id].nil?
|
|
90
|
+
build(model)
|
|
91
|
+
else
|
|
92
|
+
self.class._find(id: @guarded_write[:id]).data
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
70
96
|
def typecast(name, value, flag)
|
|
71
97
|
att = get_attr!(name, flag, request: true)
|
|
72
98
|
type_name = att[:type]
|
data/lib/graphiti/schema.rb
CHANGED
data/lib/graphiti/schema_diff.rb
CHANGED
|
@@ -102,6 +102,10 @@ module Graphiti
|
|
|
102
102
|
next
|
|
103
103
|
end
|
|
104
104
|
|
|
105
|
+
if new_rel[:guard] && !old_rel[:guard]
|
|
106
|
+
@errors << "#{old_resource[:name]}: relationship #{name.inspect} became guarded."
|
|
107
|
+
end
|
|
108
|
+
|
|
105
109
|
if new_rel[:resource] != old_rel[:resource]
|
|
106
110
|
@errors << "#{old_resource[:name]}: relationship #{name.inspect} changed resource from #{old_rel[:resource]} to #{new_rel[:resource]}."
|
|
107
111
|
end
|
data/lib/graphiti/serializer.rb
CHANGED
|
@@ -28,6 +28,9 @@ module Graphiti
|
|
|
28
28
|
prev = Util::Hash.deep_dup(field_condition_blocks)
|
|
29
29
|
super
|
|
30
30
|
self.field_condition_blocks = prev
|
|
31
|
+
# _register_condition mutates the hash, which ancestors share by
|
|
32
|
+
# reference until reassigned - copy first so the guard doesn't leak.
|
|
33
|
+
self.relationship_condition_blocks = Util::Hash.deep_dup(relationship_condition_blocks)
|
|
31
34
|
_register_condition(relationship_condition_blocks, name, options)
|
|
32
35
|
end
|
|
33
36
|
|
data/lib/graphiti/sideload.rb
CHANGED
|
@@ -99,11 +99,15 @@ module Graphiti
|
|
|
99
99
|
end
|
|
100
100
|
|
|
101
101
|
def readable?
|
|
102
|
-
|
|
102
|
+
evaluate_flag @readable
|
|
103
103
|
end
|
|
104
104
|
|
|
105
105
|
def writable?
|
|
106
|
-
|
|
106
|
+
evaluate_flag @writable
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def guarded?
|
|
110
|
+
dynamic_flag?(@readable) || dynamic_flag?(@writable)
|
|
107
111
|
end
|
|
108
112
|
|
|
109
113
|
def single?
|
|
@@ -454,20 +458,50 @@ module Graphiti
|
|
|
454
458
|
Util::Class.namespace_for(klass)
|
|
455
459
|
end
|
|
456
460
|
|
|
457
|
-
|
|
461
|
+
def dynamic_flag?(flag)
|
|
462
|
+
flag.is_a?(Symbol) || flag.is_a?(String) || flag.is_a?(Proc)
|
|
463
|
+
end
|
|
464
|
+
|
|
465
|
+
# The guard method may be defined on either resource. The declaring side
|
|
466
|
+
# wins (`has_many :positions, readable: :admin?` calls EmployeeResource#admin?,
|
|
467
|
+
# matching how attribute guards resolve). If the related resource is the only place
|
|
468
|
+
# that defines it, the guard runs there. For instance, PositionResource#admin? can guard
|
|
469
|
+
# every relationship that points at positions.
|
|
470
|
+
|
|
458
471
|
def evaluate_flag(flag)
|
|
459
472
|
return false if flag.blank?
|
|
460
473
|
|
|
461
|
-
case flag
|
|
462
|
-
when
|
|
463
|
-
|
|
464
|
-
when
|
|
465
|
-
|
|
474
|
+
case flag
|
|
475
|
+
when Symbol, String
|
|
476
|
+
guard_resource(flag).send(flag)
|
|
477
|
+
when Proc
|
|
478
|
+
evaluate_guard_proc(flag)
|
|
466
479
|
else
|
|
467
480
|
!!flag
|
|
468
481
|
end
|
|
469
482
|
end
|
|
470
483
|
|
|
484
|
+
# Private methods are eligible - guards are often not part of a
|
|
485
|
+
# resource's public API.
|
|
486
|
+
def guard_resource(method_name)
|
|
487
|
+
if !parent_resource.respond_to?(method_name, true) &&
|
|
488
|
+
resource.respond_to?(method_name, true)
|
|
489
|
+
resource
|
|
490
|
+
else
|
|
491
|
+
parent_resource
|
|
492
|
+
end
|
|
493
|
+
end
|
|
494
|
+
|
|
495
|
+
# A proc can't be introspected the way a method name can, so run it
|
|
496
|
+
# against the declaring resource and only reach for the related one if
|
|
497
|
+
# that resource turns out not to define what the proc references.
|
|
498
|
+
def evaluate_guard_proc(flag)
|
|
499
|
+
parent_resource.instance_exec(&flag)
|
|
500
|
+
rescue NameError => error
|
|
501
|
+
raise error unless resource.respond_to?(error.name, true)
|
|
502
|
+
resource.instance_exec(&flag)
|
|
503
|
+
end
|
|
504
|
+
|
|
471
505
|
def context
|
|
472
506
|
Graphiti.context[:object]
|
|
473
507
|
end
|
|
@@ -50,14 +50,41 @@ module Graphiti
|
|
|
50
50
|
end
|
|
51
51
|
end
|
|
52
52
|
|
|
53
|
+
# Guards may optionally accept the model being written and the attribute
|
|
54
|
+
# name, mirroring readable guards.
|
|
53
55
|
def guard_passes?
|
|
54
|
-
|
|
56
|
+
result = case guard_arity
|
|
57
|
+
when 0 then call_guard
|
|
58
|
+
when 1 then call_guard(resource.guard_model)
|
|
59
|
+
else call_guard(resource.guard_model, name.to_s)
|
|
60
|
+
end
|
|
61
|
+
!!result
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def call_guard(*args)
|
|
65
|
+
if guard.is_a?(Proc)
|
|
66
|
+
resource.instance_exec(*args, &guard)
|
|
67
|
+
else
|
|
68
|
+
resource.send(guard, *args)
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def guard_arity
|
|
73
|
+
method = guard.is_a?(Proc) ? guard : resource.method(guard)
|
|
74
|
+
# Negative arity means optional or splatted params - hand over
|
|
75
|
+
# everything and let the guard take what it wants.
|
|
76
|
+
method.arity.negative? ? 2 : method.arity
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def guard
|
|
80
|
+
attribute[flag]
|
|
55
81
|
end
|
|
56
82
|
|
|
57
83
|
def guarded?
|
|
58
|
-
request?
|
|
59
|
-
|
|
60
|
-
|
|
84
|
+
return false unless request?
|
|
85
|
+
return false if guard == :required
|
|
86
|
+
|
|
87
|
+
guard.is_a?(Symbol) || guard.is_a?(String) || guard.is_a?(Proc)
|
|
61
88
|
end
|
|
62
89
|
|
|
63
90
|
def supported?
|
|
@@ -19,8 +19,7 @@ module Graphiti
|
|
|
19
19
|
private
|
|
20
20
|
|
|
21
21
|
def apply?(sideload)
|
|
22
|
-
@serializer.relationship_blocks[sideload.name].nil?
|
|
23
|
-
sideload.readable?
|
|
22
|
+
@serializer.relationship_blocks[sideload.name].nil?
|
|
24
23
|
end
|
|
25
24
|
end
|
|
26
25
|
|
|
@@ -32,7 +31,8 @@ module Graphiti
|
|
|
32
31
|
end
|
|
33
32
|
|
|
34
33
|
def apply
|
|
35
|
-
@
|
|
34
|
+
sideload = @sideload
|
|
35
|
+
@serializer.relationship(@sideload.name, if: -> { sideload.readable? }, &block)
|
|
36
36
|
end
|
|
37
37
|
|
|
38
38
|
# If we can't eagerly validate links on app boot, we do it at runtime
|
data/lib/graphiti/version.rb
CHANGED
data/lib/graphiti.rb
CHANGED
|
@@ -62,6 +62,18 @@ module Graphiti
|
|
|
62
62
|
@resources ||= []
|
|
63
63
|
end
|
|
64
64
|
|
|
65
|
+
# Every guarded relationship, e.g. ["EmployeeResource.positions"]
|
|
66
|
+
def self.guarded_relationships
|
|
67
|
+
::Rails.application.eager_load! if defined?(::Rails) && ::Rails.respond_to?(:application) && ::Rails.application
|
|
68
|
+
|
|
69
|
+
resources.flat_map { |resource_class|
|
|
70
|
+
resource_class.config[:sideloads]
|
|
71
|
+
.select { |_name, sideload| sideload.guarded? }
|
|
72
|
+
.keys
|
|
73
|
+
.map { |relationship_name| "#{resource_class.name || "(anonymous resource)"}.#{relationship_name}" }
|
|
74
|
+
}.uniq.sort
|
|
75
|
+
end
|
|
76
|
+
|
|
65
77
|
def self.broadcast(name, payload)
|
|
66
78
|
# AS::N prefers domain naming format with more specific towards end
|
|
67
79
|
name = "#{name}.graphiti"
|
data/package.json
CHANGED
|
@@ -37,43 +37,66 @@
|
|
|
37
37
|
{
|
|
38
38
|
"releaseRules": [
|
|
39
39
|
{
|
|
40
|
-
"
|
|
40
|
+
"breakingMarker": "!",
|
|
41
41
|
"release": "major"
|
|
42
42
|
},
|
|
43
|
+
{
|
|
44
|
+
"breaking": true,
|
|
45
|
+
"release": "major"
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"revert": true,
|
|
49
|
+
"release": "patch"
|
|
50
|
+
},
|
|
43
51
|
{
|
|
44
52
|
"type": "feat",
|
|
45
53
|
"release": "minor"
|
|
46
54
|
},
|
|
47
55
|
{
|
|
48
|
-
"type": "
|
|
56
|
+
"type": "fix",
|
|
49
57
|
"release": "patch"
|
|
50
58
|
},
|
|
51
59
|
{
|
|
52
|
-
"type": "
|
|
60
|
+
"type": "perf",
|
|
53
61
|
"release": "patch"
|
|
54
62
|
},
|
|
63
|
+
{
|
|
64
|
+
"type": "build",
|
|
65
|
+
"release": false
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
"type": "ci",
|
|
69
|
+
"release": false
|
|
70
|
+
},
|
|
55
71
|
{
|
|
56
72
|
"type": "chore",
|
|
57
|
-
"release":
|
|
73
|
+
"release": false
|
|
58
74
|
},
|
|
59
75
|
{
|
|
60
76
|
"type": "docs",
|
|
61
|
-
"release":
|
|
77
|
+
"release": false
|
|
62
78
|
},
|
|
63
79
|
{
|
|
64
80
|
"type": "refactor",
|
|
65
|
-
"release":
|
|
81
|
+
"release": false
|
|
66
82
|
},
|
|
67
83
|
{
|
|
68
84
|
"type": "style",
|
|
69
|
-
"release":
|
|
85
|
+
"release": false
|
|
70
86
|
},
|
|
71
87
|
{
|
|
72
88
|
"type": "test",
|
|
73
|
-
"release":
|
|
89
|
+
"release": false
|
|
74
90
|
}
|
|
75
91
|
],
|
|
76
92
|
"parserOpts": {
|
|
93
|
+
"headerPattern": "^(\\w*)(?:\\((.*)\\))?(!)?: (.*)$",
|
|
94
|
+
"headerCorrespondence": [
|
|
95
|
+
"type",
|
|
96
|
+
"scope",
|
|
97
|
+
"breakingMarker",
|
|
98
|
+
"subject"
|
|
99
|
+
],
|
|
77
100
|
"noteKeywords": [
|
|
78
101
|
"BREAKING CHANGE",
|
|
79
102
|
"BREAKING CHANGES",
|
|
@@ -85,7 +108,20 @@
|
|
|
85
108
|
}
|
|
86
109
|
}
|
|
87
110
|
],
|
|
88
|
-
|
|
111
|
+
[
|
|
112
|
+
"@semantic-release/release-notes-generator",
|
|
113
|
+
{
|
|
114
|
+
"parserOpts": {
|
|
115
|
+
"headerPattern": "^(\\w*)(?:\\((.*)\\))?(!)?: (.*)$",
|
|
116
|
+
"headerCorrespondence": [
|
|
117
|
+
"type",
|
|
118
|
+
"scope",
|
|
119
|
+
"breakingMarker",
|
|
120
|
+
"subject"
|
|
121
|
+
]
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
],
|
|
89
125
|
[
|
|
90
126
|
"@semantic-release/changelog",
|
|
91
127
|
{
|
|
@@ -99,7 +135,8 @@
|
|
|
99
135
|
"@semantic-release/git",
|
|
100
136
|
{
|
|
101
137
|
"assets": [
|
|
102
|
-
"CHANGELOG.md"
|
|
138
|
+
"CHANGELOG.md",
|
|
139
|
+
"lib/graphiti/version.rb"
|
|
103
140
|
],
|
|
104
141
|
"message": "${nextRelease.version} CHANGELOG [skip ci]\n\n${nextRelease.notes}"
|
|
105
142
|
}
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: graphiti
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.12.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Lee Richmond
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-07-
|
|
11
|
+
date: 2026-07-29 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: jsonapi-serializable
|
|
@@ -242,6 +242,7 @@ files:
|
|
|
242
242
|
- Appraisals
|
|
243
243
|
- CHANGELOG.md
|
|
244
244
|
- CODE_OF_CONDUCT.md
|
|
245
|
+
- CONTRIBUTING.md
|
|
245
246
|
- Gemfile
|
|
246
247
|
- Guardfile
|
|
247
248
|
- LICENSE.txt
|
|
@@ -370,7 +371,14 @@ homepage: https://github.com/graphiti-api/graphiti
|
|
|
370
371
|
licenses:
|
|
371
372
|
- MIT
|
|
372
373
|
metadata: {}
|
|
373
|
-
post_install_message:
|
|
374
|
+
post_install_message: "Graphiti: relationship readable:/writable: guards are now enforced.\n\nSymbols,
|
|
375
|
+
strings, and procs passed to a relationship's readable/writable\nguards were always
|
|
376
|
+
interpreted as true and never actually called before this\nconditional relationship
|
|
377
|
+
change. They are now evaluated per-request, where\nunreadable relationships are
|
|
378
|
+
omitted from responses and includes, and unwritable\nrelationships reject writes.\n\nTo
|
|
379
|
+
list every affected relationship in your app:\n\n bin/rails runner 'puts Graphiti.guarded_relationships'\n\nAudit
|
|
380
|
+
these before deploying. Apps using schema.json will also see affected \nrelationships
|
|
381
|
+
flagged as \"became guarded\" by the schema check.\n"
|
|
374
382
|
rdoc_options: []
|
|
375
383
|
require_paths:
|
|
376
384
|
- lib
|