jets 3.1.5 → 3.2.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fc8976a98ceaa55f97512b3b0e78cec5f28e1d0ade29d23270894cbcfa1ef5c3
4
- data.tar.gz: 149caed9e86daea3f45e745e6f32c28aa0a15b6b938237305e41bc85c774adc4
3
+ metadata.gz: 8fedba281ee9d6ac31a44184b49bbc8fe15d309a3f4d58142cfa25c4ddebc153
4
+ data.tar.gz: e73ee950202ea1786c881b43abc95c03bc4c3e7ef44b04265997eca2f4b9211b
5
5
  SHA512:
6
- metadata.gz: e9a49470b973e8b927c5921a5e373f18d66141d2af0c288fa6a2d0cac4f59fa7a534e1e52bf949d56482f8987843b9f5628d3699f0c27cf4c9cc9f271d6a33c8
7
- data.tar.gz: 43e8734b1f3e7b2a7cfb16a1c4d74957fac5e4c1deb090d816d003655775374a8e3c03d473dbd7bc2166a05412562d74f811fe6dbe944237a958c9428f76167a
6
+ metadata.gz: 57070368542b8a767fbf8553f0c627b36432e691ae07f1f41488a0d8af830bffd1ed84a71633fe971df6b45bcf79ff8862f2d4513d22e4a1981b633f36cbb8bd
7
+ data.tar.gz: 32ba83eb4d4e87a8c92b5d7ec8d573ae2b36ff558fedbae7bb33f1ebe5dbcb36baf2f64b89b2a05d2e56e52bcd2145964a0770f7a56efdb48d1a925498c7f177
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 2.7.5
1
+ 2.7.7
data/CHANGELOG.md CHANGED
@@ -3,6 +3,21 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  This project *loosely tries* to adhere to [Semantic Versioning](http://semver.org/).
5
5
 
6
+ ## [3.2.1] - 2023-05-03
7
+ - [#637](https://github.com/boltops-tools/jets/pull/637) Readme: Replace "splat out" with "had" a baby
8
+ - [#638](https://github.com/boltops-tools/jets/pull/638) feature: update an array of reserved variables in aws lambda
9
+ - [#639](https://github.com/boltops-tools/jets/pull/639) fix assets upload during deploy
10
+
11
+ ## [3.2.0] - 2022-12-03
12
+ - [#631](https://github.com/boltops-tools/jets/pull/631) Implement vpc_endpoint_ids configuration
13
+ - [#634](https://github.com/boltops-tools/jets/pull/634) Custom domain messaging
14
+ - [#635](https://github.com/boltops-tools/jets/pull/635) smarter apigw routes paging calculation
15
+ - Introduce the storage of APIGW routes state in the jets managed s3 bucket under path jets/state/apigw/pages.json and jets/state/apigw/routes.json
16
+ - This speeds up deployment for apps with a large number of routes. Depending on the internet connection this can be minutes faster.
17
+ - Update internationalization.md
18
+ - fix domain.name check when APIGW changes
19
+ - improve change apigw endpoint change messaging
20
+
6
21
  ## [3.1.5] - 2022-08-08
7
22
  - [#628](https://github.com/boltops-tools/jets/pull/628) Multiple Databases
8
23
  - [#629](https://github.com/boltops-tools/jets/pull/629) loosen zeitwerk dependency in generated project Gemfile
data/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
  <a href="http://rubyonjets.com"><img src="http://rubyonjets.com/img/logos/jets-logo-full.png" /></a>
3
3
  </div>
4
4
 
5
- Ruby and Lambda splat out a baby and that child's name is [Jets](http://rubyonjets.com/).
5
+ Ruby and Lambda had a baby and that child's name is [Jets](http://rubyonjets.com/).
6
6
 
7
7
  ![Build Status](https://codebuild.us-west-2.amazonaws.com/badges?uuid=eyJlbmNyeXB0ZWREYXRhIjoiZ08vK2hjOHczQUVoUDhSYnBNNUU4T0gxQWJuOTlLaXpwVGQ1NjJ3NnVDY1dSdFVXQ3d2VXVSQzRFcU1qd1JPMndFZlByRktIcTUrZm5GWlM5dHpjM1ZrPSIsIml2UGFyYW1ldGVyU3BlYyI6Imluc1Qrd25GanhUdHlidjUiLCJtYXRlcmlhbFNldFNlcmlhbCI6MX0%3D&branch=master)
8
8
  [![CircleCI](https://circleci.com/gh/boltops-tools/jets.svg?style=svg)](https://circleci.com/gh/boltops-tools/jets)
@@ -16,10 +16,6 @@ Please **watch/star** this repo to help grow and support the project.
16
16
 
17
17
  **Upgrading**: If you are upgrading Jets, please check on the [Upgrading Notes](http://rubyonjets.com/docs/extras/upgrading/).
18
18
 
19
- ## Sponsors
20
-
21
- [![](https://img.boltops.com/boltops/tools/jets/sponsors/arist.png)](https://arist.co/)
22
-
23
19
  ## What is Ruby on Jets?
24
20
 
25
21
  Jets is a Ruby Serverless Framework. Jets allows you to create serverless applications with a beautiful language: Ruby. It includes everything required to build an application and deploy it to AWS Lambda.
data/backers.md CHANGED
@@ -11,10 +11,5 @@ Funds donated via Patreon go directly to support Tung Nguyen's full-time work on
11
11
  <h2 align="center">Backers via Patreon</h2>
12
12
 
13
13
  <!--10 start-->
14
- - Erlend Finvåg
15
- - Nate Clark
16
- - Hirokatsu Endo
17
- - Michael Choi
18
- - Phan Lam
19
14
  - Theron Welch
20
15
  <!--10 end-->
@@ -99,6 +99,7 @@ class Jets::Application
99
99
  config.api.cors_authorization_type = nil # nil so ApiGateway::Cors#cors_authorization_type handles
100
100
  config.api.endpoint_policy = nil # required when endpoint_type is EDGE
101
101
  config.api.endpoint_type = 'EDGE' # PRIVATE, EDGE, REGIONAL
102
+ config.api.vpc_endpoint_ids = nil
102
103
 
103
104
  config.api.authorizers = ActiveSupport::OrderedOptions.new
104
105
  config.api.authorizers.default_token_source = "Auth" # method.request.header.Auth
@@ -86,10 +86,18 @@ module Jets::AwsServices
86
86
  retry_limit: 7, # default: 3
87
87
  retry_base_delay: 0.6, # default: 0.3
88
88
  }
89
+ # See debug logger. Noisy.
90
+ # Example:
91
+ # D, [2022-12-02T13:18:55.298788 #26182] DEBUG -- : [Aws::APIGateway::Client 200 0.030837 0 retries] get_method(rest_api_id:"mke40eh6l0",resource_id:"zf8w2m",http_method:"GET")
89
92
  options.merge!(
90
93
  log_level: :debug,
91
94
  logger: Logger.new($stdout),
92
95
  ) if ENV['JETS_DEBUG_AWS_SDK']
96
+ # https://docs.aws.amazon.com/sdk-for-ruby/v3/developer-guide/debugging.html to enable http_wire_trace
97
+ # See the HTTP headers and JSON responses. Super noisy.
98
+ options.merge!(
99
+ http_wire_trace: true,
100
+ ) if ENV['JETS_DEBUG_AWS_SDK_HTTP_WIRE_TRACE']
93
101
  options
94
102
  end
95
103
  end
@@ -49,7 +49,7 @@ module Jets::Cfn::Builders
49
49
 
50
50
  def add_route53_dns
51
51
  dns = Jets::Resource::Route53::RecordSet.new
52
- if !existing_domain_name?(dns.domain_name) or existing_dns_record_on_stack?
52
+ if !existing_domain_name?(dns.domain_name) or existing_dns_record_on_stack?
53
53
  add_resource(dns)
54
54
  add_outputs(dns.outputs)
55
55
  end
@@ -57,11 +57,11 @@ module Jets::Cfn::Builders
57
57
 
58
58
  def create_domain_name()
59
59
  resource = Jets::Resource::ApiGateway::DomainName.new
60
-
60
+
61
61
  return {
62
62
  "DomainName" => resource.domain_name
63
63
  } if (existing_domain_name?(resource) and !existing_domain_name_on_stack?)
64
-
64
+
65
65
  add_resource(resource)
66
66
  return resource.outputs
67
67
  end
@@ -114,8 +114,9 @@ module Jets::Cfn::Builders
114
114
  def add_gateway_routes
115
115
  # Reject homepage. Otherwise we have 200 - 1 resources on the first page.
116
116
  # There's a next call in ApiResources.add_gateway_resources to skip the homepage.
117
- all_paths = Jets::Router.all_paths.reject { |p| p == '' }
118
- all_paths.each_slice(PAGE_LIMIT).each_with_index do |paths, i|
117
+ page_builder = Jets::Cfn::Builders::PageBuilder.new
118
+ pages = page_builder.build
119
+ pages.each_with_index do |paths, i|
119
120
  ApiResourcesBuilder.new(@options, paths, i+1).build
120
121
  end
121
122
  end
@@ -0,0 +1,80 @@
1
+ module Jets::Cfn::Builders
2
+ class PageBuilder
3
+ extend Memoist
4
+ cattr_reader :pages
5
+
6
+ # Build page slices
7
+ def build
8
+ map = build_map
9
+ pages = []
10
+ map.each do |path, existing_page|
11
+ if existing_page
12
+ pages[existing_page] ||= []
13
+ pages[existing_page] << path
14
+ end
15
+ end
16
+
17
+ # Remove existing paths from map. Leave behind new paths
18
+ pages.each do |page|
19
+ page.each do |i|
20
+ map.delete(i)
21
+ end
22
+ end
23
+
24
+ # Fill up available space in each page so all existing pages are full
25
+ keys = map.keys
26
+ pages.each do |page|
27
+ break if keys.empty?
28
+ while page.size < page_limit
29
+ path = keys.shift
30
+ break if path.nil?
31
+ page << path
32
+ end
33
+ end
34
+
35
+ # Add remaining slices to new additional pages
36
+ pages += keys.each_slice(page_limit).to_a
37
+
38
+ @@pages = pages
39
+
40
+ pages
41
+ end
42
+
43
+ # Build map that has paths as keys and page number as value
44
+ # Example: {"a1"=>0, "a2"=>0, "b1"=>1, "b2"=>1, "c1"=>2, "c2"=>2}
45
+ def build_map
46
+ map = {}
47
+ new_paths.each do |path|
48
+ map[path] = find_page_index(path)
49
+ end
50
+ map
51
+ end
52
+
53
+ def find_page_index(new_path)
54
+ pages = old_pages || []
55
+ pages.each_with_index do |slice, i|
56
+ slice.find do |old_path|
57
+ return i if old_path == new_path
58
+ end
59
+ end
60
+ nil
61
+ end
62
+
63
+ def old_pages
64
+ state = Jets::Router::State.new
65
+ state.load("pages")
66
+ end
67
+ memoize :old_pages
68
+
69
+ def new_paths
70
+ Jets::Router.all_paths.reject { |p| p == '' }
71
+ end
72
+
73
+ # Relevant is CloudFormation Outputs limit is 200
74
+ # JETS_APIGW_PAGE_LIMIT is based on that
75
+ # https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cloudformation-limits.html
76
+ def page_limit
77
+ Integer(ENV['JETS_APIGW_PAGE_LIMIT'] || 200)
78
+ end
79
+ end
80
+ end
@@ -26,7 +26,26 @@ module Jets::Cfn::Builders
26
26
  add_description("Jets: #{Jets.version} Code: #{Util::Source.version}")
27
27
 
28
28
  # Initial s3 bucket, used to store code zipfile and templates Jets generates
29
+ #
30
+ # AWS changed the default behavior of s3 buckets to block public access
31
+ # https://aws.amazon.com/blogs/aws/amazon-s3-block-public-access-another-layer-of-protection-for-your-accounts-and-buckets/
32
+ # https://github.com/aws-amplify/amplify-cli/issues/12503
33
+ #
34
+ # Jets uploads assets to s3 bucket with acl: "public-read" here
35
+ # https://github.com/boltops-tools/jets/blob/c5858ec2706a606665a92c3ada3f16ae4c753372/lib/jets/cfn/upload.rb#L97
36
+ #
37
+ # Use minimal s3 bucket policy to allow public read access to assets.
38
+ # Leave the other options as comments to help document the default behavior.
29
39
  resource = Jets::Resource::S3::Bucket.new(logical_id: "s3_bucket",
40
+ PublicAccessBlockConfiguration: {
41
+ BlockPublicAcls: false,
42
+ # BlockPublicPolicy: false,
43
+ # IgnorePublicAcls: false,
44
+ # RestrictPublicBuckets: false
45
+ },
46
+ OwnershipControls: {
47
+ Rules: [{ObjectOwnership: "ObjectWriter"}]
48
+ },
30
49
  bucket_encryption: {
31
50
  server_side_encryption_configuration: [
32
51
  server_side_encryption_by_default: {
data/lib/jets/cfn/ship.rb CHANGED
@@ -42,6 +42,7 @@ module Jets::Cfn
42
42
  exit 1
43
43
  end
44
44
 
45
+ save_apigw_state
45
46
  prewarm
46
47
  clean_deploy_logs
47
48
  show_api_endpoint
@@ -88,6 +89,12 @@ module Jets::Cfn
88
89
  Jets::Cfn::Status.new(@options).wait
89
90
  end
90
91
 
92
+ def save_apigw_state
93
+ state = Jets::Router::State.new
94
+ state.save("pages", Jets::Cfn::Builders::PageBuilder.pages)
95
+ state.save("routes", Jets::Router.routes)
96
+ end
97
+
91
98
  def prewarm
92
99
  if ENV['SKIP_PREWARMING']
93
100
  puts "Skipping prewarming" # useful for testing
@@ -8,8 +8,7 @@ class Jets::Resource::ApiGateway::RestApi::LogicalId
8
8
  end
9
9
 
10
10
  def custom_domain
11
- api = Jets::Resource::ApiGateway::DomainName.new
12
- domain_name = api.domain_name
11
+ domain_name = Jets.config.domain.name
13
12
  if domain_name
14
13
  <<~EOL
15
14
  It looks like you have already set up a custom domain.
@@ -25,7 +24,18 @@ class Jets::Resource::ApiGateway::RestApi::LogicalId
25
24
  More info: custom domain docs: https://rubyonjets.com/docs/routing/custom-domain/
26
25
  EOL
27
26
  else
28
- "Please set up a custom domain https://rubyonjets.com/docs/routing/custom-domain/"
27
+ <<~EOL
28
+ To avoid this prompt in the future, you can configure:
29
+
30
+ config/application.rb
31
+
32
+ config.api.auto_replace = true
33
+
34
+ However, you should also set up a custom domain for a "stable" endpoint.
35
+
36
+ https://rubyonjets.com/docs/routing/custom-domain/
37
+
38
+ EOL
29
39
  end
30
40
  end
31
41
 
@@ -7,51 +7,22 @@ class Jets::Resource::ApiGateway::RestApi::Routes::Change
7
7
  new.changed?
8
8
  end
9
9
 
10
- # Build up deployed routes from the existing CloudFormation resources.
10
+ # Recreate routes from previously deployed stored state in s3
11
11
  def deployed_routes
12
- routes = []
13
-
14
- resources, position = [], true
15
- while position
16
- position = nil if position == true # start of loop
17
- resp = apigateway.get_resources(
18
- rest_api_id: rest_api_id,
19
- position: position,
20
- limit: 500, # default: 25 max: 500
12
+ state = Jets::Router::State.new
13
+ data = state.load("routes")
14
+ return [] if data.nil?
15
+
16
+ data.map do |item|
17
+ Jets::Router::Route.new(
18
+ path: item['path'],
19
+ method: item['options']['method'],
20
+ to: item['to'],
21
21
  )
22
- resources += resp.items
23
- position = resp.position
24
- end
25
-
26
- resources.each do |resource|
27
- resource_methods = resource.resource_methods
28
- next if resource_methods.nil?
29
-
30
- resource_methods.each do |http_verb, resource_method|
31
- # puts "#{http_verb} #{resource.path} | resource.id #{resource.id}"
32
- # puts to(resource.id, http_verb)
33
-
34
- # Test changing config.cors and CloudFormation does an in-place update
35
- # on the resource. So no need to do bluegreen deployments for OPTIONS.
36
- next if http_verb == "OPTIONS"
37
-
38
- path = recreate_path(resource.path)
39
- method = http_verb.downcase.to_sym
40
- to = to(resource.id, http_verb)
41
- route = Jets::Router::Route.new(path: path, method: method, to: to)
42
- routes << route
43
- end
44
22
  end
45
- routes
46
23
  end
47
24
  memoize :deployed_routes
48
25
 
49
- def recreate_path(path)
50
- path = path.gsub(%r{^/},'')
51
- path = path.gsub(/{([^}]*)\+}/, '*\1')
52
- path.gsub(/{([^}]*)}/, ':\1')
53
- end
54
-
55
26
  def to(resource_id, http_method)
56
27
  uri = method_uri(resource_id, http_method)
57
28
  recreate_to(uri) unless uri.nil?
@@ -5,6 +5,7 @@ module Jets::Resource::ApiGateway
5
5
  name: Jets::Naming.gateway_api_name,
6
6
  endpoint_configuration: { types: endpoint_types }
7
7
  }
8
+ properties[:endpoint_configuration][:vpc_endpoint_ids] = vpce_ids if vpce_ids
8
9
  properties[:binary_media_types] = binary_media_types if binary_media_types
9
10
  properties[:policy] = endpoint_policy if endpoint_policy
10
11
 
@@ -60,5 +61,14 @@ module Jets::Resource::ApiGateway
60
61
 
61
62
  endpoint_policy
62
63
  end
64
+
65
+ private
66
+
67
+ def vpce_ids
68
+ ids = Jets.config.api.vpc_endpoint_ids
69
+ return nil if ids.nil? || ids.empty?
70
+
71
+ ids
72
+ end
63
73
  end
64
- end
74
+ end
@@ -36,21 +36,23 @@ class Jets::Resource::Lambda::Function
36
36
  exit 1
37
37
  end
38
38
 
39
- # https://docs.aws.amazon.com/lambda/latest/dg/current-supported-versions.html#lambda-environment-variables
39
+ # https://docs.aws.amazon.com/lambda/latest/dg/configuration-envvars.html
40
40
  def reserved_variables
41
41
  %w[
42
42
  _HANDLER
43
+ AWS_DEFAULT_REGION
43
44
  AWS_REGION
44
45
  AWS_EXECUTION_ENV
45
46
  AWS_LAMBDA_FUNCTION_NAME
46
47
  AWS_LAMBDA_FUNCTION_MEMORY_SIZE
47
48
  AWS_LAMBDA_FUNCTION_VERSION
49
+ AWS_LAMBDA_INITIALIZATION_TYPE
48
50
  AWS_LAMBDA_LOG_GROUP_NAME
49
51
  AWS_LAMBDA_LOG_STREAM_NAME
52
+ AWS_ACCESS_KEY
50
53
  AWS_ACCESS_KEY_ID
51
54
  AWS_SECRET_ACCESS_KEY
52
55
  AWS_SESSION_TOKEN
53
- TZ
54
56
  LAMBDA_TASK_ROOT
55
57
  LAMBDA_RUNTIME_DIR
56
58
  AWS_LAMBDA_RUNTIME_API
@@ -191,6 +191,10 @@ class Jets::Router
191
191
  @options[:mount_class]
192
192
  end
193
193
 
194
+ def to_h
195
+ JSON.load(to_json)
196
+ end
197
+
194
198
  private
195
199
  def ensure_jets_format(path)
196
200
  path.split('/').map do |s|
@@ -0,0 +1,47 @@
1
+ class Jets::Router
2
+ class State
3
+ extend Memoist
4
+ include Jets::AwsServices
5
+
6
+ def load(filename)
7
+ resp = s3.get_object(
8
+ bucket: Jets.aws.s3_bucket,
9
+ key: s3_storage_path(filename),
10
+ )
11
+ text = resp.body.read
12
+ JSON.load(text)
13
+ rescue
14
+ end
15
+ memoize :load
16
+
17
+ # Save previously deployed APIGW routes state
18
+ def save(filename, data)
19
+ # body = Jets::Router.routes.to_json
20
+ # body = JSON.generate(Jets::Cfn::Builders::PageBuilder.pages)
21
+ body = data.respond_to?(:to_json) ? data.to_json : JSON.generate(data)
22
+ s3.put_object(
23
+ body: body,
24
+ bucket: Jets.aws.s3_bucket,
25
+ key: s3_storage_path(filename),
26
+ )
27
+ end
28
+
29
+ # Examples:
30
+ #
31
+ # jets/state/apigw/pages.json
32
+ # jets/state/apigw/routes.json
33
+ #
34
+ # Fetch or loaded in:
35
+ #
36
+ # pages.json: Jets::Cfn::Builders::PageBuilder#old_pages
37
+ # routes.json: Jets::Resource::ApiGateway::RestApi::Routes::Change::Base#deployed_routes
38
+ #
39
+ # Saved in:
40
+ #
41
+ # Jets::Cfn::Ship#save_apigw_state
42
+ #
43
+ def s3_storage_path(filename)
44
+ "jets/state/apigw/#{filename}.json"
45
+ end
46
+ end
47
+ end
data/lib/jets/router.rb CHANGED
@@ -134,6 +134,18 @@ module Jets
134
134
  drawn_router.routes
135
135
  end
136
136
 
137
+ # So we can save state in s3 post deploy. Example of structure.
138
+ #
139
+ # [
140
+ # {"scope"=>{"options"=>{"as"=>"posts", "prefix"=>"posts", "param"=>nil, "from"=>"resources"}, "parent"=>{"options"=>{}, "parent"=>nil, "level"=>1}, "level"=>2}, "options"=>{"to"=>"posts#index", "from_scope"=>true, "path"=>"posts", "method"=>"get"}, "path"=>"posts", "to"=>"posts#index", "as"=>"posts"},
141
+ # {"scope"=>{"options"=>{"as"=>"posts", "prefix"=>"posts", "param"=>nil, "from"=>"resources"}, "parent"=>{"options"=>{}, "parent"=>nil, "level"=>1}, "level"=>2}, "options"=>{"to"=>"posts#new", "from_scope"=>true, "path"=>"posts/new", "method"=>"get"}, "path"=>"posts/new", "to"=>"posts#new", "as"=>"new_post"},
142
+ # ...
143
+ # ]
144
+ #
145
+ def to_json
146
+ JSON.dump(routes.map(&:to_h))
147
+ end
148
+
137
149
  # Returns all paths including subpaths.
138
150
  # Example:
139
151
  # Input: ["posts/:id/edit"]
data/lib/jets/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Jets
2
- VERSION = "3.1.5"
2
+ VERSION = "3.2.1"
3
3
  end
data/lib/jets.rb CHANGED
@@ -17,6 +17,7 @@ require "active_support/ordered_options"
17
17
  require "cfn_camelizer"
18
18
  require "cfn_status"
19
19
  require "fileutils"
20
+ require "json"
20
21
  require "memoist"
21
22
  require "rainbow/ext/string"
22
23
  require "serverlessgems"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jets
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.1.5
4
+ version: 3.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tung Nguyen
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-08-08 00:00:00.000000000 Z
11
+ date: 2023-05-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: actionmailer
@@ -661,6 +661,7 @@ files:
661
661
  - lib/jets/cfn/builders/function_builder.rb
662
662
  - lib/jets/cfn/builders/interface.rb
663
663
  - lib/jets/cfn/builders/job_builder.rb
664
+ - lib/jets/cfn/builders/page_builder.rb
664
665
  - lib/jets/cfn/builders/parent_builder.rb
665
666
  - lib/jets/cfn/builders/parent_builder/stagger.rb
666
667
  - lib/jets/cfn/builders/rule_builder.rb
@@ -986,6 +987,7 @@ files:
986
987
  - lib/jets/router/route.rb
987
988
  - lib/jets/router/route/authorizer.rb
988
989
  - lib/jets/router/scope.rb
990
+ - lib/jets/router/state.rb
989
991
  - lib/jets/router/util.rb
990
992
  - lib/jets/rule/base.rb
991
993
  - lib/jets/rule/dsl.rb
@@ -1101,7 +1103,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
1101
1103
  - !ruby/object:Gem::Version
1102
1104
  version: '0'
1103
1105
  requirements: []
1104
- rubygems_version: 3.3.10
1106
+ rubygems_version: 3.4.6
1105
1107
  signing_key:
1106
1108
  specification_version: 4
1107
1109
  summary: Ruby Serverless Framework