diff --git a/gems/actioncable/README.md b/gems/actioncable/README.md
index 1af531e1..7b2c9a6e 100644
--- a/gems/actioncable/README.md
+++ b/gems/actioncable/README.md
@@ -13,6 +13,7 @@ If you have any questions, please contact the maintainers. Refer to `_reviewers.
## Target gems
- actioncable
+- actionmailbox
- actionmailer
- actionpack
- actiontext
diff --git a/gems/actionmailbox/.rubocop.yml b/gems/actionmailbox/.rubocop.yml
new file mode 100644
index 00000000..74cedeae
--- /dev/null
+++ b/gems/actionmailbox/.rubocop.yml
@@ -0,0 +1,8 @@
+inherit_from: ../../.rubocop.yml
+
+RBS/Layout:
+ Enabled: true
+RBS/Lint:
+ Enabled: true
+RBS/Style:
+ Enabled: true
diff --git a/gems/actionmailbox/8.1/_test/test.rb b/gems/actionmailbox/8.1/_test/test.rb
new file mode 100644
index 00000000..73c6025f
--- /dev/null
+++ b/gems/actionmailbox/8.1/_test/test.rb
@@ -0,0 +1,42 @@
+# Write Ruby code to test the RBS.
+# It is type checked by `steep check` command.
+
+require "actionmailbox"
+
+class ForwardsMailbox < ActionMailbox::Base
+ before_processing :require_sender
+
+ def process
+ mail.subject
+ inbound_email.message_id.size
+ bounced! unless inbound_email.processed?
+ end
+
+ private
+
+ def require_sender
+ end
+end
+
+class ApplicationMailbox < ActionMailbox::Base
+ routing all: :forwards
+end
+
+inbound_email = ActionMailbox::InboundEmail.new
+inbound_email.status.to_sym
+inbound_email.message_id.size
+inbound_email.message_checksum.size
+inbound_email.created_at.to_date
+inbound_email.raw_email.attached?
+inbound_email.delivered! unless inbound_email.bounced? || inbound_email.failed?
+inbound_email.processing! if inbound_email.pending?
+inbound_email.mail.subject
+inbound_email.source.size
+inbound_email.processed? || ForwardsMailbox.receive(inbound_email)
+
+class ForwardsMailboxTest < ActionMailbox::TestCase
+ def test_receive
+ receive_inbound_email_from_mail(to: "save@example.com", subject: "Hello")
+ create_inbound_email_from_mail(to: "save@example.com")
+ end
+end
diff --git a/gems/actionmailbox/8.1/_test/test.rbs b/gems/actionmailbox/8.1/_test/test.rbs
new file mode 100644
index 00000000..593c1ee8
--- /dev/null
+++ b/gems/actionmailbox/8.1/_test/test.rbs
@@ -0,0 +1,14 @@
+class ForwardsMailbox < ActionMailbox::Base
+ def process: () -> void
+
+ private
+
+ def require_sender: () -> void
+end
+
+class ApplicationMailbox < ActionMailbox::Base
+end
+
+class ForwardsMailboxTest < ActionMailbox::TestCase
+ def test_receive: () -> void
+end
diff --git a/gems/actionmailbox/8.1/actionmailbox-generated.rbs b/gems/actionmailbox/8.1/actionmailbox-generated.rbs
new file mode 100644
index 00000000..7bcd382f
--- /dev/null
+++ b/gems/actionmailbox/8.1/actionmailbox-generated.rbs
@@ -0,0 +1,988 @@
+# The generated code is based on Ruby on Rails source code
+# You can find the license of Ruby on Rails from following.
+
+#MIT License
+#
+#Copyright (c) 37signals LLC
+#
+#Permission is hereby granted, free of charge, to any person obtaining a copy
+#of this software and associated documentation files (the "Software"), to deal
+#in the Software without restriction, including without limitation the rights
+#to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+#copies of the Software, and to permit persons to whom the Software is
+#furnished to do so, subject to the following conditions:
+#
+#The above copyright notice and this permission notice shall be included in all
+#copies or substantial portions of the Software.
+#
+#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+#IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+#FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+#AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+#LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+#OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+#SOFTWARE.
+
+module ActionMailbox
+ def self.ingress: () -> untyped
+
+ def self.ingress=: (untyped value) -> untyped
+
+ def ingress: () -> untyped
+
+ def ingress=: (untyped value) -> untyped
+
+ def self.logger: () -> untyped
+
+ def self.logger=: (untyped value) -> untyped
+
+ def logger: () -> untyped
+
+ def logger=: (untyped value) -> untyped
+
+ def self.incinerate: () -> untyped
+
+ def self.incinerate=: (untyped value) -> untyped
+
+ def incinerate: () -> untyped
+
+ def incinerate=: (untyped value) -> untyped
+
+ def self.incinerate_after: () -> untyped
+
+ def self.incinerate_after=: (untyped value) -> untyped
+
+ def incinerate_after: () -> untyped
+
+ def incinerate_after=: (untyped value) -> untyped
+
+ def self.queues: () -> untyped
+
+ def self.queues=: (untyped value) -> untyped
+
+ def queues: () -> untyped
+
+ def queues=: (untyped value) -> untyped
+
+ def self.storage_service: () -> untyped
+
+ def self.storage_service=: (untyped value) -> untyped
+
+ def storage_service: () -> untyped
+
+ def storage_service=: (untyped value) -> untyped
+
+ # The base class for all Action Mailbox ingress controllers.
+ class BaseController < ::ActionController::Base
+ private
+
+ def ensure_configured: () -> (untyped | nil)
+
+ def ingress_name: () -> untyped
+
+ def authenticate_by_password: () -> untyped
+
+ def password: () -> untyped
+ end
+end
+
+module ActionMailbox
+ # Ingests inbound emails from Mailgun. Requires the following parameters:
+ #
+ # - +body-mime+: The full RFC 822 message
+ # - +timestamp+: The current time according to Mailgun as the number of seconds passed since the UNIX epoch
+ # - +token+: A randomly-generated, 50-character string
+ # - +signature+: A hexadecimal HMAC-SHA256 of the timestamp concatenated with the token, generated using the Mailgun Signing key
+ #
+ # Authenticates requests by validating their signatures.
+ #
+ # Returns:
+ #
+ # - 204 No Content if an inbound email is successfully recorded and enqueued for routing to the appropriate mailbox
+ # - 401 Unauthorized if the request's signature could not be validated, or if its timestamp is more than 2 minutes old
+ # - 404 Not Found if Action Mailbox is not configured to accept inbound emails from Mailgun
+ # - 422 Unprocessable Entity if the request is missing required parameters
+ # - 500 Server Error if the Mailgun Signing key is missing, or one of the Active Record database,
+ # the Active Storage service, or the Active Job backend is misconfigured or unavailable
+ #
+ # == Usage
+ #
+ # 1. Give Action Mailbox your Mailgun Signing key (which you can find under Settings -> Security & Users -> API security in Mailgun)
+ # so it can authenticate requests to the Mailgun ingress.
+ #
+ # Use bin/rails credentials:edit to add your Signing key to your application's encrypted credentials under
+ # +action_mailbox.mailgun_signing_key+, where Action Mailbox will automatically find it:
+ #
+ # action_mailbox:
+ # mailgun_signing_key: ...
+ #
+ # Alternatively, provide your Signing key in the +MAILGUN_INGRESS_SIGNING_KEY+ environment variable.
+ #
+ # 2. Tell Action Mailbox to accept emails from Mailgun:
+ #
+ # # config/environments/production.rb
+ # config.action_mailbox.ingress = :mailgun
+ #
+ # 3. {Configure Mailgun}[https://documentation.mailgun.com/en/latest/user_manual.html#receiving-forwarding-and-storing-messages]
+ # to forward inbound emails to +/rails/action_mailbox/mailgun/inbound_emails/mime+.
+ #
+ # If your application lived at https://example.com, you would specify the fully-qualified URL
+ # https://example.com/rails/action_mailbox/mailgun/inbound_emails/mime.
+ class Ingresses::Mailgun::InboundEmailsController < ::ActionMailbox::BaseController
+ def create: () -> untyped
+
+ private
+
+ def mail: () -> untyped
+
+ def authenticate: () -> (untyped | nil)
+
+ def authenticated?: () -> untyped
+
+ def key: () -> untyped
+
+ class Authenticator
+ attr_reader key: untyped
+
+ attr_reader timestamp: untyped
+
+ attr_reader token: untyped
+
+ attr_reader signature: untyped
+
+ def initialize: (key: untyped, timestamp: untyped, token: untyped, signature: untyped) -> void
+
+ def authenticated?: () -> untyped
+
+ private
+
+ def signed?: () -> untyped
+
+ # Allow for 2 minutes of drift between Mailgun time and local server time.
+ def recent?: () -> untyped
+
+ def expected_signature: () -> untyped
+ end
+ end
+end
+
+module ActionMailbox
+ # Ingests inbound emails from Mandrill.
+ #
+ # Requires a +mandrill_events+ parameter containing a JSON array of Mandrill inbound email event objects.
+ # Each event is expected to have a +msg+ object containing a full RFC 822 message in its +raw_msg+ property.
+ #
+ # Returns:
+ #
+ # - 204 No Content if an inbound email is successfully recorded and enqueued for routing to the appropriate mailbox
+ # - 401 Unauthorized if the request's signature could not be validated
+ # - 404 Not Found if Action Mailbox is not configured to accept inbound emails from Mandrill
+ # - 422 Unprocessable Entity if the request is missing required parameters
+ # - 500 Server Error if the Mandrill API key is missing, or one of the Active Record database,
+ # the Active Storage service, or the Active Job backend is misconfigured or unavailable
+ class Ingresses::Mandrill::InboundEmailsController < ::ActionMailbox::BaseController
+ def create: () -> untyped
+
+ def health_check: () -> untyped
+
+ private
+
+ def raw_emails: () -> untyped
+
+ def events: () -> untyped
+
+ def authenticate: () -> (untyped | nil)
+
+ def authenticated?: () -> untyped
+
+ def key: () -> untyped
+
+ class Authenticator
+ attr_reader request: untyped
+
+ attr_reader key: untyped
+
+ def initialize: (untyped request, untyped key) -> void
+
+ def authenticated?: () -> untyped
+
+ private
+
+ def given_signature: () -> untyped
+
+ def expected_signature: () -> untyped
+
+ def message: () -> untyped
+ end
+ end
+end
+
+module ActionMailbox
+ # Ingests inbound emails from Postmark. Requires a +RawEmail+ parameter containing a full RFC 822 message.
+ #
+ # Authenticates requests using HTTP basic access authentication. The username is always +actionmailbox+, and the
+ # password is read from the application's encrypted credentials or an environment variable. See the Usage section below.
+ #
+ # Note that basic authentication is insecure over unencrypted HTTP. An attacker that intercepts cleartext requests to
+ # the Postmark ingress can learn its password. You should only use the Postmark ingress over HTTPS.
+ #
+ # Returns:
+ #
+ # - 204 No Content if an inbound email is successfully recorded and enqueued for routing to the appropriate mailbox
+ # - 401 Unauthorized if the request's signature could not be validated
+ # - 404 Not Found if Action Mailbox is not configured to accept inbound emails from Postmark
+ # - 422 Unprocessable Entity if the request is missing the required +RawEmail+ parameter
+ # - 500 Server Error if the ingress password is not configured, or if one of the Active Record database,
+ # the Active Storage service, or the Active Job backend is misconfigured or unavailable
+ #
+ # == Usage
+ #
+ # 1. Tell Action Mailbox to accept emails from Postmark:
+ #
+ # # config/environments/production.rb
+ # config.action_mailbox.ingress = :postmark
+ #
+ # 2. Generate a strong password that Action Mailbox can use to authenticate requests to the Postmark ingress.
+ #
+ # Use bin/rails credentials:edit to add the password to your application's encrypted credentials under
+ # +action_mailbox.ingress_password+, where Action Mailbox will automatically find it:
+ #
+ # action_mailbox:
+ # ingress_password: ...
+ #
+ # Alternatively, provide the password in the +RAILS_INBOUND_EMAIL_PASSWORD+ environment variable.
+ #
+ # 3. {Configure Postmark}[https://postmarkapp.com/manual#configure-your-inbound-webhook-url] to forward inbound emails
+ # to +/rails/action_mailbox/postmark/inbound_emails+ with the username +actionmailbox+ and the password you
+ # previously generated. If your application lived at https://example.com, you would configure your
+ # Postmark inbound webhook with the following fully-qualified URL:
+ #
+ # https://actionmailbox:PASSWORD@example.com/rails/action_mailbox/postmark/inbound_emails
+ #
+ # *NOTE:* When configuring your Postmark inbound webhook, be sure to check the box labeled *"Include raw email
+ # content in JSON payload"*. Action Mailbox needs the raw email content to work.
+ class Ingresses::Postmark::InboundEmailsController < ::ActionMailbox::BaseController
+ def create: () -> untyped
+
+ private
+
+ def mail: () -> untyped
+ end
+end
+
+module ActionMailbox
+ # Ingests inbound emails relayed from an SMTP server.
+ #
+ # Authenticates requests using HTTP basic access authentication. The username is always +actionmailbox+, and the
+ # password is read from the application's encrypted credentials or an environment variable. See the Usage section below.
+ #
+ # Note that basic authentication is insecure over unencrypted HTTP. An attacker that intercepts cleartext requests to
+ # the ingress can learn its password. You should only use this ingress over HTTPS.
+ #
+ # Returns:
+ #
+ # - 204 No Content if an inbound email is successfully recorded and enqueued for routing to the appropriate mailbox
+ # - 401 Unauthorized if the request could not be authenticated
+ # - 404 Not Found if Action Mailbox is not configured to accept inbound emails relayed from an SMTP server
+ # - 415 Unsupported Media Type if the request does not contain an RFC 822 message
+ # - 500 Server Error if the ingress password is not configured, or if one of the Active Record database,
+ # the Active Storage service, or the Active Job backend is misconfigured or unavailable
+ #
+ # == Usage
+ #
+ # 1. Tell Action Mailbox to accept emails from an SMTP relay:
+ #
+ # # config/environments/production.rb
+ # config.action_mailbox.ingress = :relay
+ #
+ # 2. Generate a strong password that Action Mailbox can use to authenticate requests to the ingress.
+ #
+ # Use bin/rails credentials:edit to add the password to your application's encrypted credentials under
+ # +action_mailbox.ingress_password+, where Action Mailbox will automatically find it:
+ #
+ # action_mailbox:
+ # ingress_password: ...
+ #
+ # Alternatively, provide the password in the +RAILS_INBOUND_EMAIL_PASSWORD+ environment variable.
+ #
+ # 3. Configure your SMTP server to pipe inbound emails to the appropriate ingress command, providing the +URL+ of the
+ # relay ingress and the +INGRESS_PASSWORD+ you previously generated.
+ #
+ # If your application lives at https://example.com, you would configure the Postfix SMTP server to pipe
+ # inbound emails to the following command:
+ #
+ # $ bin/rails action_mailbox:ingress:postfix URL=https://example.com/rails/action_mailbox/postfix/inbound_emails INGRESS_PASSWORD=...
+ #
+ # Built-in ingress commands are available for these popular SMTP servers:
+ #
+ # - Exim (bin/rails action_mailbox:ingress:exim)
+ # - Postfix (bin/rails action_mailbox:ingress:postfix)
+ # - Qmail (bin/rails action_mailbox:ingress:qmail)
+ class Ingresses::Relay::InboundEmailsController < ::ActionMailbox::BaseController
+ def create: () -> untyped
+
+ private
+
+ def require_valid_rfc822_message: () -> (untyped | nil)
+ end
+end
+
+module ActionMailbox
+ # Ingests inbound emails from SendGrid. Requires an +email+ parameter containing a full RFC 822 message.
+ #
+ # Authenticates requests using HTTP basic access authentication. The username is always +actionmailbox+, and the
+ # password is read from the application's encrypted credentials or an environment variable. See the Usage section below.
+ #
+ # Note that basic authentication is insecure over unencrypted HTTP. An attacker that intercepts cleartext requests to
+ # the SendGrid ingress can learn its password. You should only use the SendGrid ingress over HTTPS.
+ #
+ # Returns:
+ #
+ # - 204 No Content if an inbound email is successfully recorded and enqueued for routing to the appropriate mailbox
+ # - 401 Unauthorized if the request's signature could not be validated
+ # - 404 Not Found if Action Mailbox is not configured to accept inbound emails from SendGrid
+ # - 422 Unprocessable Entity if the request is missing the required +email+ parameter
+ # - 500 Server Error if the ingress password is not configured, or if one of the Active Record database,
+ # the Active Storage service, or the Active Job backend is misconfigured or unavailable
+ #
+ # == Usage
+ #
+ # 1. Tell Action Mailbox to accept emails from SendGrid:
+ #
+ # # config/environments/production.rb
+ # config.action_mailbox.ingress = :sendgrid
+ #
+ # 2. Generate a strong password that Action Mailbox can use to authenticate requests to the SendGrid ingress.
+ #
+ # Use bin/rails credentials:edit to add the password to your application's encrypted credentials under
+ # +action_mailbox.ingress_password+, where Action Mailbox will automatically find it:
+ #
+ # action_mailbox:
+ # ingress_password: ...
+ #
+ # Alternatively, provide the password in the +RAILS_INBOUND_EMAIL_PASSWORD+ environment variable.
+ #
+ # 3. {Configure SendGrid Inbound Parse}[https://sendgrid.com/docs/for-developers/parsing-email/setting-up-the-inbound-parse-webhook/]
+ # to forward inbound emails to +/rails/action_mailbox/sendgrid/inbound_emails+ with the username +actionmailbox+ and
+ # the password you previously generated. If your application lived at https://example.com, you would
+ # configure SendGrid with the following fully-qualified URL:
+ #
+ # https://actionmailbox:PASSWORD@example.com/rails/action_mailbox/sendgrid/inbound_emails
+ #
+ # *NOTE:* When configuring your SendGrid Inbound Parse webhook, be sure to check the box labeled *"Post the raw,
+ # full MIME message."* Action Mailbox needs the raw MIME message to work.
+ class Ingresses::Sendgrid::InboundEmailsController < ::ActionMailbox::BaseController
+ def create: () -> untyped
+
+ private
+
+ def mail: () -> untyped
+
+ def envelope: () -> untyped
+ end
+end
+
+module Rails
+ class Conductor::ActionMailbox::InboundEmailsController < ::Rails::Conductor::BaseController
+ def index: () -> untyped
+
+ def new: () -> nil
+
+ def show: () -> untyped
+
+ def create: () -> untyped
+
+ private
+
+ def new_mail: () -> untyped
+
+ def mail_params: () -> untyped
+
+ def create_inbound_email: (untyped mail) -> untyped
+ end
+end
+
+module Rails
+ class Conductor::ActionMailbox::InboundEmails::SourcesController < ::Rails::Conductor::BaseController
+ def new: () -> nil
+
+ def create: () -> untyped
+ end
+end
+
+module Rails
+ # Incinerating will destroy an email that is due and has already been processed.
+ class Conductor::ActionMailbox::IncineratesController < ::Rails::Conductor::BaseController
+ def create: () -> untyped
+ end
+end
+
+module Rails
+ # Rerouting will run routing and processing on an email that has already been, or attempted to be, processed.
+ class Conductor::ActionMailbox::ReroutesController < ::Rails::Conductor::BaseController
+ def create: () -> untyped
+
+ private
+
+ def reroute: (untyped inbound_email) -> untyped
+ end
+end
+
+module Rails
+ # TODO: Move this to Rails::Conductor gem
+ class Conductor::BaseController < ::ActionController::Base
+ private
+
+ def ensure_development_env: () -> (untyped | nil)
+ end
+end
+
+module ActionMailbox
+ # You can configure when this +IncinerationJob+ will be run as a time-after-processing using the
+ # +config.action_mailbox.incinerate_after+ or +ActionMailbox.incinerate_after+ setting.
+ #
+ # Since this incineration is set for the future, it'll automatically ignore any InboundEmails
+ # that have already been deleted and discard itself if so.
+ #
+ # You can disable incinerating processed emails by setting +config.action_mailbox.incinerate+ or
+ # +ActionMailbox.incinerate+ to +false+.
+ class IncinerationJob < ::ActiveJob::Base
+ def self.schedule: (untyped inbound_email) -> untyped
+
+ def perform: (untyped inbound_email) -> untyped
+ end
+end
+
+module ActionMailbox
+ # Routing a new InboundEmail is an asynchronous operation, which allows the ingress controllers to quickly
+ # accept new incoming emails without being burdened to hang while they're actually being processed.
+ class RoutingJob < ::ActiveJob::Base
+ def perform: (untyped inbound_email) -> untyped
+ end
+end
+
+module ActionMailbox
+ # The +InboundEmail+ is an Active Record that keeps a reference to the raw email stored in Active Storage
+ # and tracks the status of processing. By default, incoming emails will go through the following lifecycle:
+ #
+ # * Pending: Just received by one of the ingress controllers and scheduled for routing.
+ # * Processing: During active processing, while a specific mailbox is running its #process method.
+ # * Delivered: Successfully processed by the specific mailbox.
+ # * Failed: An exception was raised during the specific mailbox's execution of the +#process+ method.
+ # * Bounced: Rejected processing by the specific mailbox and bounced to sender.
+ #
+ # Once the +InboundEmail+ has reached the status of being either +delivered+, +failed+, or +bounced+,
+ # it'll count as having been +#processed?+. Once processed, the +InboundEmail+ will be scheduled for
+ # automatic incineration at a later point.
+ #
+ # When working with an +InboundEmail+, you'll usually interact with the parsed version of the source,
+ # which is available as a +Mail+ object from +#mail+. But you can also access the raw source directly
+ # using the +#source+ method.
+ #
+ # Examples:
+ #
+ # inbound_email.mail.from # => 'david@loudthinking.com'
+ # inbound_email.source # Returns the full rfc822 source of the email as text
+ class InboundEmail < ::ActionMailbox::Record
+ extend ::ActionMailbox::InboundEmail::MessageId::ClassMethods
+
+ include ::ActionMailbox::InboundEmail::Incineratable
+
+ include ::ActionMailbox::InboundEmail::MessageId
+
+ include ::ActionMailbox::InboundEmail::Routable
+
+ def instrumentation_payload: () -> { id: untyped, message_id: untyped, status: untyped }
+ end
+end
+
+# Ensure that the +InboundEmail+ is automatically scheduled for later incineration if the status has been
+# changed to +processed+. The later incineration will be invoked at the time specified by the
+# +ActionMailbox.incinerate_after+ time using the +IncinerationJob+.
+module ActionMailbox::InboundEmail::Incineratable
+ extend ::ActiveSupport::Concern
+
+ def incinerate_later: () -> untyped
+
+ def incinerate: () -> untyped
+end
+
+module ActionMailbox
+ # Command class for carrying out the actual incineration of the +InboundMail+ that's been scheduled
+ # for removal. Before the incineration – which really is just a call to +#destroy!+ – is run, we verify
+ # that it's both eligible (by virtue of having already been processed) and time to do so (that is,
+ # the +InboundEmail+ was processed after the +incinerate_after+ time).
+ class InboundEmail::Incineratable::Incineration
+ def initialize: (untyped inbound_email) -> void
+
+ def run: () -> (untyped | nil)
+
+ private
+
+ def due?: () -> untyped
+
+ def processed?: () -> untyped
+ end
+end
+
+# The +Message-ID+ as specified by rfc822 is supposed to be a unique identifier for that individual email.
+# That makes it an ideal tracking token for debugging and forensics, just like +X-Request-Id+ does for
+# web request.
+#
+# If an inbound email does not, against the rfc822 mandate, specify a Message-ID, one will be generated
+# using the approach from +Mail::MessageIdField+.
+module ActionMailbox::InboundEmail::MessageId
+ extend ::ActiveSupport::Concern
+end
+
+# A newly received +InboundEmail+ will not be routed synchronously as part of ingress controller's receival.
+# Instead, the routing will be done asynchronously, using a +RoutingJob+, to ensure maximum parallel capacity.
+#
+# By default, all newly created +InboundEmail+ records that have the status of +pending+, which is the default,
+# will be scheduled for automatic, deferred routing.
+module ActionMailbox::InboundEmail::Routable
+ extend ::ActiveSupport::Concern
+
+ # Enqueue a +RoutingJob+ for this +InboundEmail+.
+ def route_later: () -> untyped
+
+ # Route this +InboundEmail+ using the routing rules declared on the +ApplicationMailbox+.
+ def route: () -> untyped
+end
+
+module ActionMailbox
+ class Record < ::ActiveRecord::Base
+ end
+end
+
+# :markup: markdown
+# :include: ../README.md
+module ActionMailbox
+ extend ::ActiveSupport::Autoload
+end
+
+module ActionMailbox
+ # = Action Mailbox \Base
+ #
+ # The base class for all application mailboxes. Not intended to be inherited from directly. Inherit from
+ # +ApplicationMailbox+ instead, as that's where the app-specific routing is configured. This routing
+ # is specified in the following ways:
+ #
+ # class ApplicationMailbox < ActionMailbox::Base
+ # # Any of the recipients of the mail (whether to, cc, bcc) are matched against the regexp.
+ # routing /^replies@/i => :replies
+ #
+ # # Any of the recipients of the mail (whether to, cc, bcc) needs to be an exact match for the string.
+ # routing "help@example.com" => :help
+ #
+ # # Any callable (proc, lambda, etc) object is passed the inbound_email record and is a match if true.
+ # routing ->(inbound_email) { inbound_email.mail.to.size > 2 } => :multiple_recipients
+ #
+ # # Any object responding to #match? is called with the inbound_email record as an argument. Match if true.
+ # routing CustomAddress.new => :custom
+ #
+ # # Any inbound_email that has not been already matched will be sent to the BackstopMailbox.
+ # routing :all => :backstop
+ # end
+ #
+ # Application mailboxes need to override the #process method, which is invoked by the framework after
+ # callbacks have been run. The callbacks available are: +before_processing+, +after_processing+, and
+ # +around_processing+. The primary use case is to ensure that certain preconditions to processing are fulfilled
+ # using +before_processing+ callbacks.
+ #
+ # If a precondition fails to be met, you can halt the processing using the +#bounced!+ method,
+ # which will silently prevent any further processing, but not actually send out any bounce notice. You
+ # can also pair this behavior with the invocation of an Action Mailer class responsible for sending out
+ # an actual bounce email. This is done using the #bounce_with method, which takes the mail object returned
+ # by an Action Mailer method, like so:
+ #
+ # class ForwardsMailbox < ApplicationMailbox
+ # before_processing :ensure_sender_is_a_user
+ #
+ # private
+ # def ensure_sender_is_a_user
+ # unless User.exists?(email_address: mail.from)
+ # bounce_with UserRequiredMailer.missing(inbound_email)
+ # end
+ # end
+ # end
+ #
+ # During the processing of the inbound email, the status will be tracked. Before processing begins,
+ # the email will normally have the +pending+ status. Once processing begins, just before callbacks
+ # and the #process method is called, the status is changed to +processing+. If processing is allowed to
+ # complete, the status is changed to +delivered+. If a bounce is triggered, then +bounced+. If an unhandled
+ # exception is bubbled up, then +failed+.
+ #
+ # Exceptions can be handled at the class level using the familiar
+ # ActiveSupport::Rescuable approach:
+ #
+ # class ForwardsMailbox < ApplicationMailbox
+ # rescue_from(ApplicationSpecificVerificationError) { bounced! }
+ # end
+ class Base
+ extend ::ActionMailbox::Routing::ClassMethods
+
+ extend ::ActionMailbox::Callbacks::ClassMethods
+
+ extend ::ActiveSupport::Callbacks::ClassMethods
+
+ extend ::ActiveSupport::Rescuable::ClassMethods
+
+ def logger: (*untyped, **untyped) ?{ (*untyped) -> untyped } -> untyped
+
+ include ::ActiveSupport::Rescuable
+
+ include ::ActionMailbox::Callbacks
+
+ include ::ActionMailbox::Routing
+
+ def perform_processing: () -> untyped
+
+ def process: () -> nil
+
+ def finished_processing?: () -> untyped
+
+ # Enqueues the given +message+ for delivery and changes the inbound email's status to +:bounced+.
+ def bounce_with: (untyped message) -> untyped
+
+ # Immediately sends the given +message+ and changes the inbound email's status to +:bounced+.
+ def bounce_now_with: (untyped message) -> untyped
+
+ private
+
+ def instrumentation_payload: () -> { mailbox: self, inbound_email: untyped }
+
+ def track_status_of_inbound_email: () { () -> untyped } -> untyped
+ end
+end
+
+module ActionMailbox
+ # = Action Mailbox \Callbacks
+ #
+ # Defines the callbacks related to processing.
+ module Callbacks
+ extend ::ActiveSupport::Concern
+
+ include ::ActiveSupport::Callbacks
+
+ TERMINATOR: untyped
+ end
+end
+
+module ActionMailbox
+ def self.deprecator: () -> untyped
+end
+
+module ActionMailbox
+ class Engine < ::Rails::Engine
+ end
+end
+
+module ActionMailbox
+ # Returns the currently loaded version of Action Mailbox as a +Gem::Version+.
+ def self.gem_version: () -> untyped
+
+ module VERSION
+ MAJOR: 8
+
+ MINOR: 1
+
+ TINY: 4
+
+ PRE: nil
+
+ STRING: untyped
+ end
+end
+
+module Mail
+ class Address
+ def ==: (untyped other_address) -> untyped
+ end
+end
+
+module Mail
+ class Address
+ def self.wrap: (untyped address) -> untyped
+ end
+end
+
+module Mail
+ class Message
+ def from_address: () -> untyped
+
+ def reply_to_address: () -> untyped
+
+ def recipients_addresses: () -> untyped
+
+ def to_addresses: () -> untyped
+
+ def cc_addresses: () -> untyped
+
+ def bcc_addresses: () -> untyped
+
+ def x_original_to_addresses: () -> untyped
+
+ def x_forwarded_to_addresses: () -> untyped
+
+ private
+
+ def address_list: (untyped obj) -> untyped
+ end
+end
+
+module Mail
+ def self.from_source: (untyped source) -> untyped
+end
+
+module Mail
+ class Message
+ def recipients: () -> untyped
+ end
+end
+
+module ActionMailbox
+ class Relayer
+ class Result
+ def success?: () -> untyped
+
+ def failure?: () -> untyped
+
+ def transient_failure?: () -> untyped
+
+ def permanent_failure?: () -> untyped
+ end
+
+ CONTENT_TYPE: "message/rfc822"
+
+ USER_AGENT: ::String
+
+ attr_reader uri: untyped
+
+ attr_reader username: untyped
+
+ attr_reader password: untyped
+
+ def initialize: (url: untyped, password: untyped, ?username: ::String) -> void
+
+ def relay: (untyped source) -> untyped
+
+ private
+
+ def post: (untyped source) -> untyped
+
+ def client: () -> untyped
+ end
+end
+
+module ActionMailbox
+ # = Action Mailbox \Router
+ #
+ # Encapsulates the routes that live on the ApplicationMailbox and performs the actual routing when
+ # an inbound_email is received.
+ class Router
+ class RoutingError < ::StandardError
+ end
+
+ def initialize: () -> void
+
+ def add_routes: (untyped routes) -> untyped
+
+ def add_route: (untyped address, to: untyped) -> untyped
+
+ def route: (untyped inbound_email) -> untyped
+
+ def mailbox_for: (untyped inbound_email) -> untyped
+
+ private
+
+ attr_reader routes: untyped
+ end
+end
+
+module ActionMailbox
+ # Encapsulates a route, which can then be matched against an inbound_email and provide a lookup of the matching
+ # mailbox class. See examples for the different route addresses and how to use them in the ActionMailbox::Base
+ # documentation.
+ class Router::Route
+ attr_reader address: untyped
+
+ attr_reader mailbox_name: untyped
+
+ def initialize: (untyped address, to: untyped) -> void
+
+ def match?: (untyped inbound_email) -> untyped
+
+ def mailbox_class: () -> untyped
+
+ private
+
+ def ensure_valid_address: () -> (untyped | nil)
+ end
+end
+
+module ActionMailbox
+ # See ActionMailbox::Base for how to specify routing.
+ module Routing
+ def router: () -> untyped
+
+ def router=: (untyped value) -> untyped
+
+ extend ::ActiveSupport::Concern
+ end
+end
+
+module ActionMailbox
+ class TestCase < ::ActiveSupport::TestCase
+ include ::ActionMailbox::TestHelper
+ end
+end
+
+module ActionMailbox
+ module TestHelper
+ # Create an InboundEmail record using an eml fixture in the format of message/rfc822
+ # referenced with +fixture_name+ located in +test/fixtures/files/fixture_name+.
+ def create_inbound_email_from_fixture: (untyped fixture_name, ?status: ::Symbol) -> untyped
+
+ # Creates an InboundEmail by specifying through options or a block.
+ #
+ # ==== Options
+ #
+ # * :status - The +status+ to set for the created InboundEmail.
+ # For possible statuses, see its documentation.
+ #
+ # ==== Creating a simple email
+ #
+ # When you only need to set basic fields like +from+, +to+, +subject+, and
+ # +body+, you can pass them directly as options.
+ #
+ # create_inbound_email_from_mail(from: "david@loudthinking.com", subject: "Hello!")
+ #
+ # ==== Creating a multi-part email
+ #
+ # When you need to create a more intricate email, like a multi-part email
+ # that contains both a plaintext version and an HTML version, you can pass a
+ # block.
+ #
+ # create_inbound_email_from_mail do
+ # to "David Heinemeier Hansson "
+ # from "Bilbo Baggins "
+ # subject "Come down to the Shire!"
+ #
+ # text_part do
+ # body "Please join us for a party at Bag End"
+ # end
+ #
+ # html_part do
+ # body "Please join us for a party at Bag End
"
+ # end
+ # end
+ #
+ # As with +Mail.new+, you can also use a block parameter to define the parts
+ # of the message:
+ #
+ # create_inbound_email_from_mail do |mail|
+ # mail.to "David Heinemeier Hansson "
+ # mail.from "Bilbo Baggins "
+ # mail.subject "Come down to the Shire!"
+ #
+ # mail.text_part do |part|
+ # part.body "Please join us for a party at Bag End"
+ # end
+ #
+ # mail.html_part do |part|
+ # part.body "Please join us for a party at Bag End
"
+ # end
+ # end
+ def create_inbound_email_from_mail: (?status: ::Symbol, **untyped mail_options) ?{ (?) -> untyped } -> untyped
+
+ # Create an InboundEmail using the raw rfc822 +source+ as text.
+ def create_inbound_email_from_source: (untyped source, ?status: ::Symbol) -> untyped
+
+ # Create an InboundEmail from fixture using the same arguments as create_inbound_email_from_fixture
+ # and immediately route it to processing.
+ def receive_inbound_email_from_fixture: (*untyped args) -> untyped
+
+ # Create an InboundEmail using the same options or block as
+ # create_inbound_email_from_mail, then immediately route it for processing.
+ def receive_inbound_email_from_mail: (**untyped kwargs) ?{ (?) -> untyped } -> untyped
+
+ # Create an InboundEmail using the same arguments as create_inbound_email_from_source and immediately route it
+ # to processing.
+ def receive_inbound_email_from_source: (*untyped args) -> untyped
+ end
+end
+
+module ActionMailbox
+ # Returns the currently loaded version of Action Mailbox as a +Gem::Version+.
+ def self.version: () -> untyped
+end
+
+module ActionMailbox::InboundEmail::MessageId
+ module ClassMethods
+ def create_and_extract_message_id!: (untyped source, **untyped options) -> untyped
+
+ private
+
+ def extract_message_id: (untyped source) -> untyped
+
+ def generate_missing_message_id: (untyped message_checksum) -> untyped
+
+ def create_and_upload_raw_email!: (untyped source) -> untyped
+ end
+end
+
+module ActionMailbox
+ module Callbacks
+ module ClassMethods
+ def before_processing: (*untyped methods) ?{ (?) -> untyped } -> untyped
+
+ def after_processing: (*untyped methods) ?{ (?) -> untyped } -> untyped
+
+ def around_processing: (*untyped methods) ?{ (?) -> untyped } -> untyped
+ end
+ end
+end
+
+module ActionMailbox
+ module Routing
+ module ClassMethods
+ def router: () -> untyped
+
+ def router=: (untyped value) -> untyped
+
+ def routing: (untyped routes) -> untyped
+
+ def route: (untyped inbound_email) -> untyped
+
+ def mailbox_for: (untyped inbound_email) -> untyped
+ end
+ end
+end
+
+module ActionMailbox::Ingresses
+end
+
+module ActionMailbox::Ingresses::Mailgun
+end
+
+module ActionMailbox::Ingresses::Mandrill
+end
+
+module ActionMailbox::Ingresses::Postmark
+end
+
+module ActionMailbox::Ingresses::Relay
+end
+
+module ActionMailbox::Ingresses::Sendgrid
+end
+
+module Rails::Conductor
+end
+
+module Rails::Conductor::ActionMailbox
+end
+
+module Rails::Conductor::ActionMailbox::InboundEmails
+end
diff --git a/gems/actionmailbox/8.1/actionmailbox.rbs b/gems/actionmailbox/8.1/actionmailbox.rbs
new file mode 100644
index 00000000..e52a9242
--- /dev/null
+++ b/gems/actionmailbox/8.1/actionmailbox.rbs
@@ -0,0 +1,65 @@
+# Methods rbs prototype doesn't see, which aren't generated from the source
+module ActionMailbox
+ class InboundEmail
+ # Columns of action_mailbox_inbound_emails
+ attr_accessor status: String
+
+ attr_accessor message_id: String
+
+ attr_accessor message_checksum: String
+
+ def created_at: () -> ActiveSupport::TimeWithZone
+
+ def updated_at: () -> ActiveSupport::TimeWithZone
+
+ # has_one_attached :raw_email
+ def raw_email: () -> ActiveStorage::Attached::One
+
+ # enum :status, %i[ pending processing delivered failed bounced ]
+ def pending?: () -> bool
+
+ def pending!: () -> void
+
+ def processing?: () -> bool
+
+ def processing!: () -> void
+
+ def delivered?: () -> bool
+
+ def delivered!: () -> void
+
+ def failed?: () -> bool
+
+ def failed!: () -> void
+
+ def bounced?: () -> bool
+
+ def bounced!: () -> void
+ end
+end
+
+# Return types of commonly used methods
+module ActionMailbox
+ class Base
+ attr_reader inbound_email: ActionMailbox::InboundEmail
+
+ # Delegated to inbound_email
+ def mail: () -> Mail::Message
+
+ def delivered!: () -> void
+
+ def bounced!: () -> void
+
+ def self.receive: (ActionMailbox::InboundEmail inbound_email) -> untyped
+
+ def initialize: (ActionMailbox::InboundEmail inbound_email) -> void
+ end
+
+ class InboundEmail
+ def mail: () -> Mail::Message
+
+ def source: () -> String
+
+ def processed?: () -> bool
+ end
+end
diff --git a/gems/actionmailbox/README.md b/gems/actionmailbox/README.md
new file mode 100644
index 00000000..7b2c9a6e
--- /dev/null
+++ b/gems/actionmailbox/README.md
@@ -0,0 +1,30 @@
+Maintenance policy for RBS of Rails
+===
+
+We basically only accept changes for the versions listed under the **"List of currently supported releases"** in the [Rails' maintenance policy](https://rubyonrails.org/maintenance).
+Or support the last version maintained in this repository.
+
+Versions outside of that are generally not accepted for changes. We do not run tests for them, nor do we include them in format detection.
+
+This is a measure to reduce maintenance costs.
+
+If you have any questions, please contact the maintainers. Refer to `_reviewers.yaml` for the list of maintainers.
+
+## Target gems
+
+- actioncable
+- actionmailbox
+- actionmailer
+- actionpack
+- actiontext
+- actionview
+- activejob
+- activemodel
+- activerecord
+- activestorage
+- activesupport
+- railties
+
+## See also
+
+https://github.com/ruby/gem_rbs_collection/issues/788
diff --git a/gems/actionmailbox/_reviewers.yaml b/gems/actionmailbox/_reviewers.yaml
new file mode 100644
index 00000000..4bb54423
--- /dev/null
+++ b/gems/actionmailbox/_reviewers.yaml
@@ -0,0 +1,2 @@
+reviewers:
+ - amatsuda
diff --git a/gems/actionmailer/README.md b/gems/actionmailer/README.md
index 1af531e1..7b2c9a6e 100644
--- a/gems/actionmailer/README.md
+++ b/gems/actionmailer/README.md
@@ -13,6 +13,7 @@ If you have any questions, please contact the maintainers. Refer to `_reviewers.
## Target gems
- actioncable
+- actionmailbox
- actionmailer
- actionpack
- actiontext
diff --git a/gems/actionpack/README.md b/gems/actionpack/README.md
index 1af531e1..7b2c9a6e 100644
--- a/gems/actionpack/README.md
+++ b/gems/actionpack/README.md
@@ -13,6 +13,7 @@ If you have any questions, please contact the maintainers. Refer to `_reviewers.
## Target gems
- actioncable
+- actionmailbox
- actionmailer
- actionpack
- actiontext
diff --git a/gems/actiontext/README.md b/gems/actiontext/README.md
index 1af531e1..7b2c9a6e 100644
--- a/gems/actiontext/README.md
+++ b/gems/actiontext/README.md
@@ -13,6 +13,7 @@ If you have any questions, please contact the maintainers. Refer to `_reviewers.
## Target gems
- actioncable
+- actionmailbox
- actionmailer
- actionpack
- actiontext
diff --git a/gems/actionview/README.md b/gems/actionview/README.md
index 1af531e1..7b2c9a6e 100644
--- a/gems/actionview/README.md
+++ b/gems/actionview/README.md
@@ -13,6 +13,7 @@ If you have any questions, please contact the maintainers. Refer to `_reviewers.
## Target gems
- actioncable
+- actionmailbox
- actionmailer
- actionpack
- actiontext
diff --git a/gems/activejob/README.md b/gems/activejob/README.md
index 1af531e1..7b2c9a6e 100644
--- a/gems/activejob/README.md
+++ b/gems/activejob/README.md
@@ -13,6 +13,7 @@ If you have any questions, please contact the maintainers. Refer to `_reviewers.
## Target gems
- actioncable
+- actionmailbox
- actionmailer
- actionpack
- actiontext
diff --git a/gems/activemodel/README.md b/gems/activemodel/README.md
index 1af531e1..7b2c9a6e 100644
--- a/gems/activemodel/README.md
+++ b/gems/activemodel/README.md
@@ -13,6 +13,7 @@ If you have any questions, please contact the maintainers. Refer to `_reviewers.
## Target gems
- actioncable
+- actionmailbox
- actionmailer
- actionpack
- actiontext
diff --git a/gems/activerecord/README.md b/gems/activerecord/README.md
index 1af531e1..7b2c9a6e 100644
--- a/gems/activerecord/README.md
+++ b/gems/activerecord/README.md
@@ -13,6 +13,7 @@ If you have any questions, please contact the maintainers. Refer to `_reviewers.
## Target gems
- actioncable
+- actionmailbox
- actionmailer
- actionpack
- actiontext
diff --git a/gems/activestorage/README.md b/gems/activestorage/README.md
index 1af531e1..7b2c9a6e 100644
--- a/gems/activestorage/README.md
+++ b/gems/activestorage/README.md
@@ -13,6 +13,7 @@ If you have any questions, please contact the maintainers. Refer to `_reviewers.
## Target gems
- actioncable
+- actionmailbox
- actionmailer
- actionpack
- actiontext
diff --git a/gems/activesupport/README.md b/gems/activesupport/README.md
index 1af531e1..7b2c9a6e 100644
--- a/gems/activesupport/README.md
+++ b/gems/activesupport/README.md
@@ -13,6 +13,7 @@ If you have any questions, please contact the maintainers. Refer to `_reviewers.
## Target gems
- actioncable
+- actionmailbox
- actionmailer
- actionpack
- actiontext
diff --git a/gems/railties/README.md b/gems/railties/README.md
index 1af531e1..7b2c9a6e 100644
--- a/gems/railties/README.md
+++ b/gems/railties/README.md
@@ -13,6 +13,7 @@ If you have any questions, please contact the maintainers. Refer to `_reviewers.
## Target gems
- actioncable
+- actionmailbox
- actionmailer
- actionpack
- actiontext