light-services 3.2.0 โ†’ 3.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: eaf377a62c2b05d82860527f64df11bb2239745d2e3b6ab1bc060cddc153effc
4
- data.tar.gz: 86eb6a64e0744e6fe2f80b1b4fc06e9d38a740911f88c2b076da9e9a2aab082b
3
+ metadata.gz: 3f1c7874fc2ac069ac686f0b12895567c76c7005523dc0c1b841781f73ab3988
4
+ data.tar.gz: 4b41b830919ac007ec7740b1a84ca7d8a39682d9623fd9fd9085546e384391a3
5
5
  SHA512:
6
- metadata.gz: b11918fed0617837cd922a9c7c2181a855844d359bce30aa7f523a828e77e3605522cff64440cf200f90732cb17bbee901305a01920bde54a3cb4777b748a760
7
- data.tar.gz: aa78896f8cf22b7e2cf17853700bbe347d0dd364409ee4033064d7c4c5fa57d9377620e5ab6b29473cf15c2894e29b189aa8634e7d71ffda9ad2ca6fa6fce792
6
+ metadata.gz: eac0742e9f253e03b5e1cb58619798fc81b4f3139df62311c9350d4f3f1a13264dc4ad74af14e84033fa66137fbe31b390f95056783a83151ec76527c0590bc3
7
+ data.tar.gz: 45a9a709c76c57032c8ae71bc8fa31e32fee3aebe8e82a8c1265480c400ab2a724cfc9dc9d3572a830ff78a03235cfd94a95f782c4c92750db5d5f0d9f83bd6f
data/CHANGELOG.md CHANGED
@@ -1,6 +1,18 @@
1
1
  # Changelog
2
2
 
3
- ## 3.2.0 (2025-12-14)
3
+ ## 3.3.0 (2025-12-15)
4
+
5
+ ### Added
6
+
7
+ - Sorbet and Tapioca support for `arg` and `output`
8
+
9
+ ## 3.2.1 (2025-12-15)
10
+
11
+ ### Added
12
+
13
+ - Add RuboCop cop `ReservedName`
14
+
15
+ ## 3.2.0 (2025-12-15)
4
16
 
5
17
  ### Added
6
18
 
@@ -12,7 +24,7 @@
12
24
 
13
25
  - Service runs steps with `always: true` after `fail_immediately!` was called
14
26
 
15
- ## 3.1.2 (2025-12-13)
27
+ ## 3.1.2 (2025-12-14)
16
28
 
17
29
  ### Added
18
30
 
@@ -22,7 +34,7 @@
22
34
 
23
35
  - Split `config.require_type` into `config.require_arg_type` and `config.require_output_type`
24
36
 
25
- ## 3.1.1 (2025-12-13)
37
+ ## 3.1.1 (2025-12-14)
26
38
 
27
39
  ### Added
28
40
 
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- light-services (3.2.0)
4
+ light-services (3.3.0)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
data/README.md CHANGED
@@ -18,6 +18,7 @@ Light Services is a simple yet powerful way to organize business logic in Ruby a
18
18
  - ๐Ÿงช **RSpec Matchers**: Built-in RSpec matchers for expressive service tests
19
19
  - ๐ŸŒ **Framework Agnostic**: Compatible with Rails, Hanami, or any Ruby framework
20
20
  - ๐Ÿงฉ **Modularity**: Isolate and test your services with ease
21
+ - ๐Ÿ”ท **Sorbet & Tapioca**: Full support for Sorbet type checking and Tapioca DSL generation
21
22
  - โœ… **100% Test Coverage**: Thoroughly tested and reliable
22
23
  - โš”๏ธ **Battle-Tested**: In production use since 2017
23
24
 
data/docs/README.md CHANGED
@@ -16,6 +16,7 @@ Light Services is a simple yet powerful way to organize business logic in Ruby a
16
16
  - ๐Ÿ” **RuboCop Integration**: Custom cops to enforce best practices at lint time
17
17
  - ๐ŸŒ **Framework Agnostic**: Compatible with Rails, Hanami, or any Ruby framework
18
18
  - ๐Ÿงฉ **Modularity**: Isolate and test your services with ease
19
+ - ๐Ÿ”ท **Sorbet & Tapioca**: Full support for Sorbet type checking and Tapioca DSL generation
19
20
  - โœ… **100% Test Coverage**: Thoroughly tested and reliable
20
21
  - โš”๏ธ **Battle-Tested**: In production use since 2017
21
22
 
data/docs/SUMMARY.md CHANGED
@@ -19,6 +19,7 @@
19
19
  * [Rails Generators](generators.md)
20
20
  * [RuboCop Integration](rubocop.md)
21
21
  * [Ruby LSP Integration](ruby-lsp.md)
22
+ * [Tapioca / Sorbet Integration](tapioca.md)
22
23
 
23
24
  ## Examples
24
25
 
data/docs/tapioca.md ADDED
@@ -0,0 +1,200 @@
1
+ # Tapioca / Sorbet Integration
2
+
3
+ Light Services provides a [Tapioca](https://github.com/Shopify/tapioca) DSL compiler that generates RBI signatures for methods automatically created by the `arg` and `output` DSL macros. This enables full Sorbet type checking for your services.
4
+
5
+ ## Features
6
+
7
+ When you use the `arg` or `output` keywords, Light Services dynamically generates methods at runtime:
8
+
9
+ ```ruby
10
+ class CreateUser < ApplicationService
11
+ arg :name, type: String
12
+ arg :email, type: String, optional: true
13
+ arg :role, type: [Symbol, String]
14
+
15
+ output :user, type: User
16
+ end
17
+ ```
18
+
19
+ The Tapioca compiler generates RBI signatures for these methods:
20
+
21
+ ```rbi
22
+ # sorbet/rbi/dsl/create_user.rbi
23
+ # typed: true
24
+
25
+ class CreateUser
26
+ sig { returns(String) }
27
+ def name; end
28
+
29
+ sig { returns(T::Boolean) }
30
+ def name?; end
31
+
32
+ sig { returns(T.nilable(String)) }
33
+ def email; end
34
+
35
+ sig { returns(T::Boolean) }
36
+ def email?; end
37
+
38
+ sig { returns(T.any(Symbol, String)) }
39
+ def role; end
40
+
41
+ sig { returns(T::Boolean) }
42
+ def role?; end
43
+
44
+ sig { returns(User) }
45
+ def user; end
46
+
47
+ sig { returns(T::Boolean) }
48
+ def user?; end
49
+
50
+ private
51
+
52
+ sig { params(value: String).returns(String) }
53
+ def name=(value); end
54
+
55
+ sig { params(value: T.nilable(String)).returns(T.nilable(String)) }
56
+ def email=(value); end
57
+
58
+ sig { params(value: T.any(Symbol, String)).returns(T.any(Symbol, String)) }
59
+ def role=(value); end
60
+
61
+ sig { params(value: User).returns(User) }
62
+ def user=(value); end
63
+ end
64
+ ```
65
+
66
+ ## Setup
67
+
68
+ ### 1. Install Tapioca
69
+
70
+ Add Tapioca to your Gemfile:
71
+
72
+ ```ruby
73
+ group :development do
74
+ gem "tapioca", require: false
75
+ end
76
+ ```
77
+
78
+ Then run:
79
+
80
+ ```bash
81
+ bundle install
82
+ bundle exec tapioca init
83
+ ```
84
+
85
+ ### 2. Generate RBI Files
86
+
87
+ The Light Services compiler is automatically discovered by Tapioca. Generate RBI files with:
88
+
89
+ ```bash
90
+ bundle exec tapioca dsl
91
+ ```
92
+
93
+ This will create RBI files in `sorbet/rbi/dsl/` for all your services.
94
+
95
+ ### 3. Re-generate After Changes
96
+
97
+ After adding or modifying `arg`/`output` declarations, regenerate the RBI files:
98
+
99
+ ```bash
100
+ bundle exec tapioca dsl LightServices
101
+ ```
102
+
103
+ ## Type Mappings
104
+
105
+ ### Ruby Types
106
+
107
+ Standard Ruby types are mapped directly:
108
+
109
+ | Ruby Type | Sorbet Type |
110
+ |-----------|-------------|
111
+ | `String` | `::String` |
112
+ | `Integer` | `::Integer` |
113
+ | `Float` | `::Float` |
114
+ | `Hash` | `::Hash` |
115
+ | `Array` | `::Array` |
116
+ | `Symbol` | `::Symbol` |
117
+ | `User` (custom) | `::User` |
118
+
119
+ ### Boolean Types
120
+
121
+ Boolean types are mapped to `T::Boolean`:
122
+
123
+ ```ruby
124
+ arg :active, type: [TrueClass, FalseClass]
125
+ # Generates: sig { returns(T::Boolean) }
126
+ ```
127
+
128
+ ### Union Types
129
+
130
+ Multiple types create union types:
131
+
132
+ ```ruby
133
+ arg :id, type: [String, Integer]
134
+ # Generates: sig { returns(T.any(::String, ::Integer)) }
135
+ ```
136
+
137
+ ### Optional Types
138
+
139
+ Optional arguments/outputs are wrapped in `T.nilable`:
140
+
141
+ ```ruby
142
+ arg :nickname, type: String, optional: true
143
+ # Generates: sig { returns(T.nilable(::String)) }
144
+ ```
145
+
146
+ ### Dry-Types
147
+
148
+ If you use [dry-types](https://dry-rb.org/gems/dry-types/), they are mapped to their primitive Ruby types:
149
+
150
+ | Dry Type | Sorbet Type |
151
+ |----------|-------------|
152
+ | `Types::String` | `::String` |
153
+ | `Types::Strict::String` | `::String` |
154
+ | `Types::Integer` | `::Integer` |
155
+ | `Types::Bool` | `T::Boolean` |
156
+ | `Types::Array` | `::Array` |
157
+ | `Types::Hash` | `::Hash` |
158
+ | `Types::Date` | `::Date` |
159
+ | `Types::Time` | `::Time` |
160
+ | `Types::DateTime` | `::DateTime` |
161
+ | `Types::Decimal` | `::BigDecimal` |
162
+ | `Types::Any` | `T.untyped` |
163
+
164
+ Parameterized dry-types (e.g., `Types::Array.of(String)`) are mapped to their base type.
165
+
166
+ ## Generated Methods
167
+
168
+ For each `arg` or `output`, three methods are generated:
169
+
170
+ | Method | Return Type | Visibility |
171
+ |--------|-------------|------------|
172
+ | `name` | The declared type | public |
173
+ | `name?` | `T::Boolean` | public |
174
+ | `name=` | The declared type | **private** |
175
+
176
+ ## Inheritance
177
+
178
+ The compiler handles inherited arguments and outputs. If a child service inherits from a parent, the RBI will include methods for both parent and child fields.
179
+
180
+ ## Troubleshooting
181
+
182
+ ### RBI files not generated
183
+
184
+ Ensure Light Services is properly loaded in your application. The compiler only runs if `Light::Services::Base` is defined.
185
+
186
+ ### Types showing as `T.untyped`
187
+
188
+ This happens when:
189
+ - No `type:` option is specified for the argument/output
190
+ - The type cannot be resolved (e.g., undefined constant)
191
+
192
+ ### Custom type mappings
193
+
194
+ If you need custom dry-types mappings, you can extend the `DRY_TYPE_MAPPINGS` constant in the compiler or open an issue to add common mappings.
195
+
196
+ ## See Also
197
+
198
+ - [Ruby LSP Integration](ruby-lsp.md) - Editor integration without Sorbet
199
+ - [Arguments](arguments.md) - Full `arg` DSL documentation
200
+ - [Outputs](outputs.md) - Full `output` DSL documentation
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../../../../../light/services/constants"
4
+
5
+ module RuboCop
6
+ module Cop
7
+ module LightServices
8
+ # Ensures that `arg`, `step`, and `output` declarations do not use reserved names
9
+ # that would conflict with Light::Services methods.
10
+ #
11
+ # @example
12
+ # # bad
13
+ # arg :errors, type: Array
14
+ # arg :outputs, type: Hash
15
+ # step :call
16
+ # output :success?, type: [TrueClass, FalseClass]
17
+ #
18
+ # # good
19
+ # arg :validation_errors, type: Array
20
+ # arg :result_outputs, type: Hash
21
+ # step :execute
22
+ # output :succeeded, type: [TrueClass, FalseClass]
23
+ #
24
+ class ReservedName < Base
25
+ include RuboCop::Cop::RangeHelp
26
+
27
+ MSG = "`%<name>s` is a reserved name and cannot be used as %<field_type>s. " \
28
+ "It conflicts with Light::Services methods."
29
+
30
+ SEVERITY = :error
31
+
32
+ RESTRICT_ON_SEND = [:arg, :step, :output].freeze
33
+
34
+ FIELD_TYPE_NAMES = {
35
+ arg: "an argument",
36
+ step: "a step",
37
+ output: "an output",
38
+ }.freeze
39
+
40
+ # @!method dsl_call?(node)
41
+ def_node_matcher :dsl_call?, <<~PATTERN
42
+ (send nil? ${:arg :step :output} (sym $_) ...)
43
+ PATTERN
44
+
45
+ def on_send(node)
46
+ dsl_call?(node) do |method_name, name|
47
+ return unless Light::Services::ReservedNames::ALL.include?(name)
48
+
49
+ field_type = FIELD_TYPE_NAMES[method_name]
50
+ add_offense(node, message: format(MSG, name: name, field_type: field_type), severity: SEVERITY)
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -10,4 +10,5 @@ require_relative "rubocop/cop/light_services/missing_private_keyword"
10
10
  require_relative "rubocop/cop/light_services/no_direct_instantiation"
11
11
  require_relative "rubocop/cop/light_services/output_type_required"
12
12
  require_relative "rubocop/cop/light_services/prefer_fail_method"
13
+ require_relative "rubocop/cop/light_services/reserved_name"
13
14
  require_relative "rubocop/cop/light_services/step_method_exists"
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Light
4
4
  module Services
5
- VERSION = "3.2.0"
5
+ VERSION = "3.3.0"
6
6
  end
7
7
  end
@@ -0,0 +1,255 @@
1
+ # frozen_string_literal: true
2
+
3
+ return unless defined?(Tapioca::Dsl::Compiler)
4
+
5
+ module Tapioca
6
+ module Dsl
7
+ module Compilers
8
+ # Tapioca DSL compiler for Light::Services
9
+ #
10
+ # Generates RBI signatures for methods automatically defined by the
11
+ # `arg`/`argument` and `output` DSL macros in light-services.
12
+ #
13
+ # For each argument and output, three methods are generated:
14
+ # - Getter: `def name` - returns the value
15
+ # - Predicate: `def name?` - returns boolean
16
+ # - Setter: `def name=` (private) - sets the value
17
+ #
18
+ # @example Service definition
19
+ # class CreateUser < Light::Services::Base
20
+ # arg :name, type: String
21
+ # arg :email, type: String, optional: true
22
+ # arg :role, type: [Symbol, String]
23
+ #
24
+ # output :user, type: User
25
+ # end
26
+ #
27
+ # @example Generated RBI
28
+ # class CreateUser
29
+ # sig { returns(String) }
30
+ # def name; end
31
+ #
32
+ # sig { returns(T::Boolean) }
33
+ # def name?; end
34
+ #
35
+ # sig { returns(T.nilable(String)) }
36
+ # def email; end
37
+ #
38
+ # sig { returns(T::Boolean) }
39
+ # def email?; end
40
+ #
41
+ # sig { returns(T.any(Symbol, String)) }
42
+ # def role; end
43
+ #
44
+ # sig { returns(T::Boolean) }
45
+ # def role?; end
46
+ #
47
+ # sig { returns(User) }
48
+ # def user; end
49
+ #
50
+ # sig { returns(T::Boolean) }
51
+ # def user?; end
52
+ #
53
+ # private
54
+ #
55
+ # sig { params(value: String).returns(String) }
56
+ # def name=(value); end
57
+ #
58
+ # # ... other setters
59
+ # end
60
+ class LightServices < Compiler
61
+ extend T::Sig
62
+
63
+ # Default type mappings for common dry-types to their underlying Ruby types
64
+ DRY_TYPE_MAPPINGS = {
65
+ "Types::String" => "::String",
66
+ "Types::Strict::String" => "::String",
67
+ "Types::Coercible::String" => "::String",
68
+ "Types::Integer" => "::Integer",
69
+ "Types::Strict::Integer" => "::Integer",
70
+ "Types::Coercible::Integer" => "::Integer",
71
+ "Types::Float" => "::Float",
72
+ "Types::Strict::Float" => "::Float",
73
+ "Types::Coercible::Float" => "::Float",
74
+ "Types::Decimal" => "::BigDecimal",
75
+ "Types::Strict::Decimal" => "::BigDecimal",
76
+ "Types::Coercible::Decimal" => "::BigDecimal",
77
+ "Types::Bool" => "T::Boolean",
78
+ "Types::Strict::Bool" => "T::Boolean",
79
+ "Types::True" => "::TrueClass",
80
+ "Types::Strict::True" => "::TrueClass",
81
+ "Types::False" => "::FalseClass",
82
+ "Types::Strict::False" => "::FalseClass",
83
+ "Types::Array" => "::Array",
84
+ "Types::Strict::Array" => "::Array",
85
+ "Types::Hash" => "::Hash",
86
+ "Types::Strict::Hash" => "::Hash",
87
+ "Types::Symbol" => "::Symbol",
88
+ "Types::Strict::Symbol" => "::Symbol",
89
+ "Types::Coercible::Symbol" => "::Symbol",
90
+ "Types::Date" => "::Date",
91
+ "Types::Strict::Date" => "::Date",
92
+ "Types::DateTime" => "::DateTime",
93
+ "Types::Strict::DateTime" => "::DateTime",
94
+ "Types::Time" => "::Time",
95
+ "Types::Strict::Time" => "::Time",
96
+ "Types::Nil" => "::NilClass",
97
+ "Types::Strict::Nil" => "::NilClass",
98
+ "Types::Any" => "T.untyped",
99
+ }.freeze
100
+
101
+ ConstantType = type_member { { fixed: T.class_of(::Light::Services::Base) } }
102
+
103
+ class << self
104
+ extend T::Sig
105
+
106
+ sig { override.returns(T::Enumerable[Module]) }
107
+ def gather_constants
108
+ all_classes.select do |klass|
109
+ klass < ::Light::Services::Base && klass.name && klass != ::Light::Services::Base
110
+ end
111
+ end
112
+ end
113
+
114
+ sig { override.void }
115
+ def decorate
116
+ arguments = constant.arguments
117
+ outputs = constant.outputs
118
+
119
+ return if arguments.empty? && outputs.empty?
120
+
121
+ root.create_path(constant) do |klass|
122
+ # Generate argument methods
123
+ arguments.each_value do |field|
124
+ generate_field_methods(klass, field)
125
+ end
126
+
127
+ # Generate output methods
128
+ outputs.each_value do |field|
129
+ generate_field_methods(klass, field)
130
+ end
131
+ end
132
+ end
133
+
134
+ private
135
+
136
+ sig { params(klass: RBI::Scope, field: ::Light::Services::Settings::Field).void }
137
+ def generate_field_methods(klass, field)
138
+ name = field.name.to_s
139
+ ruby_type = resolve_type(field)
140
+ return_type = field.optional ? as_nilable_type(ruby_type) : ruby_type
141
+
142
+ # Getter
143
+ klass.create_method(name, return_type: return_type)
144
+
145
+ # Predicate
146
+ klass.create_method("#{name}?", return_type: "T::Boolean")
147
+
148
+ # Setter (private)
149
+ klass.create_method(
150
+ "#{name}=",
151
+ parameters: [create_param("value", type: return_type)],
152
+ return_type: return_type,
153
+ visibility: RBI::Private.new,
154
+ )
155
+ end
156
+
157
+ sig { params(field: ::Light::Services::Settings::Field).returns(String) }
158
+ def resolve_type(field)
159
+ type = field.instance_variable_get(:@type)
160
+ return "T.untyped" unless type
161
+
162
+ if type.is_a?(Array)
163
+ resolve_array_type(type)
164
+ elsif dry_type?(type)
165
+ resolve_dry_type(type)
166
+ elsif type.is_a?(Class) || type.is_a?(Module)
167
+ ruby_type_for_class(type)
168
+ else
169
+ "T.untyped"
170
+ end
171
+ end
172
+
173
+ sig { params(types: T::Array[T.untyped]).returns(String) }
174
+ def resolve_array_type(types)
175
+ resolved_types = types.map do |t|
176
+ if t.is_a?(Class) || t.is_a?(Module)
177
+ ruby_type_for_class(t)
178
+ elsif dry_type?(t)
179
+ resolve_dry_type(t)
180
+ else
181
+ "T.untyped"
182
+ end
183
+ end.uniq
184
+
185
+ return resolved_types.first if resolved_types.size == 1
186
+
187
+ # Check if this is a boolean type (TrueClass + FalseClass)
188
+ if resolved_types.sort == ["::FalseClass", "::TrueClass"]
189
+ "T::Boolean"
190
+ else
191
+ "T.any(#{resolved_types.join(', ')})"
192
+ end
193
+ end
194
+
195
+ sig { params(klass: T.any(Class, Module)).returns(String) }
196
+ def ruby_type_for_class(klass)
197
+ name = klass.name
198
+ return "T.untyped" unless name
199
+
200
+ # Handle boolean types specially
201
+ if klass == TrueClass
202
+ "::TrueClass"
203
+ elsif klass == FalseClass
204
+ "::FalseClass"
205
+ else
206
+ "::#{name}"
207
+ end
208
+ end
209
+
210
+ sig { params(type: T.untyped).returns(T::Boolean) }
211
+ def dry_type?(type)
212
+ return false unless defined?(Dry::Types::Type)
213
+
214
+ type.is_a?(Dry::Types::Type)
215
+ end
216
+
217
+ sig { params(type: T.untyped).returns(String) }
218
+ def resolve_dry_type(type)
219
+ type_string = type.to_s
220
+
221
+ # Direct mapping lookup
222
+ return DRY_TYPE_MAPPINGS[type_string] if DRY_TYPE_MAPPINGS.key?(type_string)
223
+
224
+ # Handle parameterized types: Types::Array.of(...) โ†’ Types::Array
225
+ base_type = type_string.split(".").first
226
+ return DRY_TYPE_MAPPINGS[base_type] if DRY_TYPE_MAPPINGS.key?(base_type)
227
+
228
+ # Try to infer from primitive
229
+ infer_from_primitive(type)
230
+ end
231
+
232
+ sig { params(type: T.untyped).returns(String) }
233
+ def infer_from_primitive(type)
234
+ return "T.untyped" unless type.respond_to?(:primitive)
235
+
236
+ primitive = type.primitive
237
+ return "T.untyped" unless primitive.is_a?(Class) || primitive.is_a?(Module)
238
+
239
+ ruby_type_for_class(primitive)
240
+ rescue StandardError
241
+ "T.untyped"
242
+ end
243
+
244
+ sig { params(type: String).returns(String) }
245
+ def as_nilable_type(type)
246
+ # Don't double-wrap nilable types
247
+ return type if type.start_with?("T.nilable(")
248
+ return type if type == "T.untyped"
249
+
250
+ "T.nilable(#{type})"
251
+ end
252
+ end
253
+ end
254
+ end
255
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: light-services
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.2.0
4
+ version: 3.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Kodkod
@@ -55,6 +55,7 @@ files:
55
55
  - docs/ruby-lsp.md
56
56
  - docs/service-rendering.md
57
57
  - docs/steps.md
58
+ - docs/tapioca.md
58
59
  - docs/testing.md
59
60
  - lib/generators/light_services/install/USAGE
60
61
  - lib/generators/light_services/install/install_generator.rb
@@ -99,6 +100,7 @@ files:
99
100
  - lib/light/services/rubocop/cop/light_services/no_direct_instantiation.rb
100
101
  - lib/light/services/rubocop/cop/light_services/output_type_required.rb
101
102
  - lib/light/services/rubocop/cop/light_services/prefer_fail_method.rb
103
+ - lib/light/services/rubocop/cop/light_services/reserved_name.rb
102
104
  - lib/light/services/rubocop/cop/light_services/step_method_exists.rb
103
105
  - lib/light/services/settings/field.rb
104
106
  - lib/light/services/settings/step.rb
@@ -107,6 +109,7 @@ files:
107
109
  - lib/ruby_lsp/light_services/addon.rb
108
110
  - lib/ruby_lsp/light_services/definition.rb
109
111
  - lib/ruby_lsp/light_services/indexing_enhancement.rb
112
+ - lib/tapioca/dsl/compilers/light_services.rb
110
113
  - light-services.gemspec
111
114
  homepage: https://light-services-docs.vercel.app/
112
115
  licenses: