standard_id 0.30.0 → 0.31.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: a4df6991c3cbb7aef140e051d72b3f935b8df67cfa2cb9464ee4edbfce15da54
4
- data.tar.gz: 77369b34dfb1704913c5a65201af083e089f32cad50cfc4e9617b1b4a2916cf6
3
+ metadata.gz: f929d1f67a0302d3b2f93043c5ac009d8d98195a8c1f3f5c7e79ca45210873dc
4
+ data.tar.gz: 1b58091aeb06c9d3a13a3fc57dd710688dd75c8be48d2d950b19bfc400cb00d0
5
5
  SHA512:
6
- metadata.gz: ccb1224de12859d408ddbdaeed0e05066195515108109f5088475d299c697f44dc6c1b80869936ea2452a8639dec1d5f922d52a94f4d48fdf2f4190d3d20e60d
7
- data.tar.gz: 1457a6e23483a48c2a84a32a2e03827da964c296f75232238d99da65995f3317b8a5622ed5fcc0180be304a166bb6b98b5baeb0390f81bfa432765049196700d
6
+ metadata.gz: b4dbbac7f24a1cb89efa9787e2acbed6ab4f8fa531d84c525344f3069c4d6bd0be2d5a17f014074f700c5af88be2b6c071ed6d1ecb94d1b27fec172bf8d3b169
7
+ data.tar.gz: 265319cf5f65851df3e4a7f8a24d3aa4765d001a27e4d89797e10cc372f4515d558b785863810cd71e7f8d577e4388c1b8f98dfe273218824e29fa7f8e3f9183
data/CHANGELOG.md CHANGED
@@ -7,6 +7,27 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.31.0] - 2026-07-27
11
+
12
+ ### Fixed
13
+
14
+ - **`EmailIdentifier` now rejects dot-atom violations in the local part.**
15
+ `URI::MailTo::EMAIL_REGEXP` models the local part as one flat character class
16
+ that happens to include `.`, so it accepted dot placements RFC 5322 forbids in
17
+ an unquoted local part — a leading dot, a trailing dot, and consecutive dots.
18
+ `a..b@example.com` passed it. Email service providers do not accept these:
19
+ Postmark rejects them at *send* time with `InvalidEmailRequestError`, so a
20
+ typo accepted at sign-up only surfaced much later as a failed delivery job,
21
+ long after the user could have corrected it. The domain pattern is
22
+ `URI::MailTo`'s own, unchanged; only the local part is stricter.
23
+
24
+ **Consumer impact.** Addresses your app previously accepted may now be
25
+ rejected at the identifier layer. The validation is scoped to a *changed*
26
+ value, so rows created under the looser rule keep saving until someone edits
27
+ the address — an existing account will not be locked out of a flow that merely
28
+ stamps `verified_at`. Check any test fixtures or seeds using addresses with
29
+ doubled or edge dots.
30
+
10
31
  ## [0.30.0] - 2026-07-26
11
32
 
12
33
  ### Added
@@ -1,7 +1,28 @@
1
1
  module StandardId
2
2
  class EmailIdentifier < Identifier
3
+ # `URI::MailTo::EMAIL_REGEXP` models the local part as one flat character
4
+ # class that happens to include `.`, so it accepts dot placements RFC 5322
5
+ # forbids in an unquoted local part: a leading dot, a trailing dot, and
6
+ # consecutive dots. `a..b@example.com` passes it.
7
+ #
8
+ # ESPs do not. Postmark rejects such an address at send time with
9
+ # `InvalidEmailRequestError`, so a typo accepted at sign-up surfaces much
10
+ # later as a failed delivery job that no operator can act on — the address
11
+ # is already stored and the user is long gone.
12
+ #
13
+ # Enforce the dot-atom rule instead: the local part is dot-separated atoms,
14
+ # each at least one character. The domain pattern is `URI::MailTo`'s,
15
+ # unchanged.
16
+ LOCAL_PART = %r{[A-Za-z0-9!\#$%&'*+/=?^_`{|}~-]+(?:\.[A-Za-z0-9!\#$%&'*+/=?^_`{|}~-]+)*}
17
+ DOMAIN = /[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?(?:\.[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?)*/
18
+ EMAIL_FORMAT = /\A#{LOCAL_PART}@#{DOMAIN}\z/
19
+
3
20
  normalizes :value, with: ->(e) { e.strip.downcase }
4
21
 
5
- validates :value, format: { with: URI::MailTo::EMAIL_REGEXP }
22
+ # Scoped to a changed value so tightening the rule cannot strand an account
23
+ # created under the looser one: an existing row keeps working until someone
24
+ # actually edits the address. Without this, saving an unrelated attribute on
25
+ # a grandfathered identifier would start raising.
26
+ validates :value, format: { with: EMAIL_FORMAT }, if: :value_changed?
6
27
  end
7
28
  end
@@ -1,3 +1,3 @@
1
1
  module StandardId
2
- VERSION = "0.30.0"
2
+ VERSION = "0.31.0"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: standard_id
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.30.0
4
+ version: 0.31.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jaryl Sim