jetstream_bridge 2.2.1 → 2.4.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.
@@ -4,43 +4,105 @@
4
4
  #
5
5
  module JetstreamBridge
6
6
  # Utility for parsing human-friendly durations into milliseconds.
7
+ #
8
+ # Defaults to an :auto heuristic for Integer/Float values to preserve
9
+ # backward compatibility:
10
+ # - Integers < 1000 are treated as seconds (e.g., 30 -> 30_000ms)
11
+ # - Integers >= 1000 are treated as milliseconds (e.g., 1500 -> 1500ms)
12
+ # Prefer setting `default_unit:` to :s or :ms for unambiguous behavior.
13
+ #
7
14
  # Examples:
8
- # Duration.to_millis(30) #=> 30000
9
- # Duration.to_millis("30s") #=> 30000
10
- # Duration.to_millis("500ms") #=> 500
11
- # Duration.to_millis(0.5) #=> 500
15
+ # Duration.to_millis(30) #=> 30000 (auto)
16
+ # Duration.to_millis(1500) #=> 1500 (auto)
17
+ # Duration.to_millis(1500, default_unit: :s) #=> 1_500_000
18
+ # Duration.to_millis("30s") #=> 30000
19
+ # Duration.to_millis("500ms") #=> 500
20
+ # Duration.to_millis("250us") #=> 0
21
+ # Duration.to_millis("1h") #=> 3_600_000
22
+ # Duration.to_millis(1_500_000_000, default_unit: :ns) #=> 1500
23
+ #
24
+ # Also:
25
+ # Duration.normalize_list_to_millis(%w[1s 5s 15s]) #=> [1000, 5000, 15000]
12
26
  module Duration
13
- MULTIPLIER = { 'ms' => 1, 's' => 1_000, 'm' => 60_000, 'h' => 3_600_000 }.freeze
14
- NUMBER_RE = /\A\d+\z/.freeze
15
- TOKEN_RE = /\A(\d+(?:\.\d+)?)\s*(ms|s|m|h)\z/i.freeze
27
+ # multipliers to convert 1 unit into milliseconds
28
+ MULTIPLIER_MS = {
29
+ 'ns' => 1.0e-6, # nanoseconds to ms
30
+ 'us' => 1.0e-3, # microseconds to ms
31
+ 'µs' => 1.0e-3, # alt microseconds symbol
32
+ 'ms' => 1, # milliseconds to ms
33
+ 's' => 1_000, # seconds to ms
34
+ 'm' => 60_000, # minutes to ms
35
+ 'h' => 3_600_000, # hours to ms
36
+ 'd' => 86_400_000 # days to ms
37
+ }.freeze
38
+
39
+ NUMBER_RE = /\A\d[\d_]*\z/.freeze
40
+ TOKEN_RE = /\A(\d[\d_]*(?:\.\d+)?)\s*(ns|us|µs|ms|s|m|h|d)\z/i.freeze
16
41
 
17
42
  module_function
18
43
 
19
- def to_millis(val)
20
- return int_to_ms(val) if val.is_a?(Integer)
21
- return float_to_ms(val) if val.is_a?(Float)
22
- return string_to_ms(val) if val.is_a?(String)
23
- return float_to_ms(val.to_f) if val.respond_to?(:to_f)
44
+ # default_unit:
45
+ # :auto (heuristic: int<1000 -> seconds, >=1000 -> ms)
46
+ # :ns, :us, :ms, :s, :m, :h, :d (explicit)
47
+ def to_millis(val, default_unit: :auto)
48
+ case val
49
+ when Integer then int_to_ms(val, default_unit: default_unit)
50
+ when Float then float_to_ms(val, default_unit: default_unit)
51
+ when String then string_to_ms(val, default_unit: default_unit == :auto ? :s : default_unit)
52
+ else
53
+ raise ArgumentError, "invalid duration type: #{val.class}" unless val.respond_to?(:to_f)
54
+
55
+ float_to_ms(val.to_f, default_unit: default_unit)
56
+
57
+ end
58
+ end
24
59
 
25
- raise ArgumentError, "invalid duration type: #{val.class}"
60
+ # Normalize an array of durations into integer milliseconds.
61
+ def normalize_list_to_millis(values, default_unit: :auto)
62
+ Array(values).map { |v| to_millis(v, default_unit: default_unit) }
26
63
  end
27
64
 
28
- def int_to_ms(i)
29
- i >= 1_000 ? i : i * 1_000
65
+ # --- internal helpers ---
66
+
67
+ def int_to_ms(num, default_unit:)
68
+ case default_unit
69
+ when :auto
70
+ # Preserve existing heuristic for compatibility
71
+ i >= 1_000 ? num : num * 1_000
72
+ else
73
+ coerce_numeric_to_ms(i.to_f, default_unit)
74
+ end
30
75
  end
31
76
 
32
- def float_to_ms(f)
33
- (f * 1_000).round
77
+ def float_to_ms(flt, default_unit:)
78
+ coerce_numeric_to_ms(flt, default_unit)
34
79
  end
35
80
 
36
- def string_to_ms(str)
81
+ def string_to_ms(str, default_unit:)
37
82
  s = str.strip
38
- return int_to_ms(s.to_i) if NUMBER_RE.match?(s)
83
+ # Plain number string => use default_unit explicitly (not heuristic)
84
+ return coerce_numeric_to_ms(s.delete('_').to_f, default_unit) if NUMBER_RE.match?(s)
39
85
 
40
86
  m = TOKEN_RE.match(s)
41
87
  raise ArgumentError, "invalid duration: #{str.inspect}" unless m
42
88
 
43
- (m[1].to_f * MULTIPLIER.fetch(m[2].downcase)).round
89
+ num = m[1].delete('_').to_f
90
+ unit = m[2].downcase
91
+ (num * MULTIPLIER_MS.fetch(unit)).round
92
+ end
93
+
94
+ def coerce_numeric_to_ms(num, unit)
95
+ case unit
96
+ when :auto
97
+ # For floats, :auto treats as seconds (common developer intent)
98
+ (num * 1_000).round
99
+ else
100
+ u = unit.to_s
101
+ mult = MULTIPLIER_MS[u]
102
+ raise ArgumentError, "invalid unit for default_unit: #{unit.inspect}" unless mult
103
+
104
+ (num * mult).round
105
+ end
44
106
  end
45
107
  end
46
108
  end
@@ -49,9 +49,12 @@ module JetstreamBridge
49
49
 
50
50
  def do_publish?(subject, envelope)
51
51
  headers = { 'nats-msg-id' => envelope['event_id'] }
52
+
52
53
  @jts.publish(subject, JSON.generate(envelope), header: headers)
53
- Logging.info("Published #{subject} event_id=#{envelope['event_id']}",
54
- tag: 'JetstreamBridge::Publisher')
54
+ Logging.info(
55
+ "Published #{subject} event_id=#{envelope['event_id']}",
56
+ tag: 'JetstreamBridge::Publisher'
57
+ )
55
58
  true
56
59
  end
57
60
 
@@ -60,8 +63,10 @@ module JetstreamBridge
60
63
  klass = ModelUtils.constantize(JetstreamBridge.config.outbox_model)
61
64
 
62
65
  unless ModelUtils.ar_class?(klass)
63
- Logging.warn("Outbox model #{klass} is not an ActiveRecord model; publishing directly.",
64
- tag: 'JetstreamBridge::Publisher')
66
+ Logging.warn(
67
+ "Outbox model #{klass} is not an ActiveRecord model; publishing directly.",
68
+ tag: 'JetstreamBridge::Publisher'
69
+ )
65
70
  return with_retries { do_publish?(subject, envelope) }
66
71
  end
67
72
 
@@ -70,8 +75,10 @@ module JetstreamBridge
70
75
  record = repo.find_or_build(event_id)
71
76
 
72
77
  if repo.already_sent?(record)
73
- Logging.info("Outbox already sent event_id=#{event_id}; skipping publish.",
74
- tag: 'JetstreamBridge::Publisher')
78
+ Logging.info(
79
+ "Outbox already sent event_id=#{event_id}; skipping publish.",
80
+ tag: 'JetstreamBridge::Publisher'
81
+ )
75
82
  return true
76
83
  end
77
84
 
@@ -102,14 +109,18 @@ module JetstreamBridge
102
109
 
103
110
  def backoff(attempts, error)
104
111
  delay = RETRY_BACKOFFS[attempts - 1] || RETRY_BACKOFFS.last
105
- Logging.warn("Publish retry #{attempts} after #{error.class}: #{error.message}",
106
- tag: 'JetstreamBridge::Publisher')
112
+ Logging.warn(
113
+ "Publish retry #{attempts} after #{error.class}: #{error.message}",
114
+ tag: 'JetstreamBridge::Publisher'
115
+ )
107
116
  sleep delay
108
117
  end
109
118
 
110
119
  def log_error(val, exc)
111
- Logging.error("Publish failed: #{exc.class} #{exc.message}",
112
- tag: 'JetstreamBridge::Publisher')
120
+ Logging.error(
121
+ "Publish failed: #{exc.class} #{exc.message}",
122
+ tag: 'JetstreamBridge::Publisher'
123
+ )
113
124
  val
114
125
  end
115
126
 
@@ -5,11 +5,82 @@ require_relative 'overlap_guard'
5
5
  require_relative 'subject_matcher'
6
6
 
7
7
  module JetstreamBridge
8
- # Ensures a stream exists and adds only subjects that are not already covered.
8
+ module StreamSupport
9
+ module_function
10
+
11
+ def normalize_subjects(list)
12
+ Array(list).flatten.compact.map!(&:to_s).reject(&:empty?).uniq
13
+ end
14
+
15
+ def missing_subjects(existing, desired)
16
+ desired.reject { |d| SubjectMatcher.covered?(existing, d) }
17
+ end
18
+
19
+ def stream_not_found?(error)
20
+ msg = error.message.to_s
21
+ msg =~ /stream\s+not\s+found/i || msg =~ /\b404\b/
22
+ end
23
+
24
+ def overlap_error?(error)
25
+ msg = error.message.to_s
26
+ msg =~ /subjects?\s+overlap/i || msg =~ /\berr_code=10065\b/ || msg =~ /\b400\b/
27
+ end
28
+
29
+ # ---- Logging ----
30
+ def log_already_covered(name)
31
+ Logging.info(
32
+ "Stream #{name} exists; subjects and config already covered.",
33
+ tag: 'JetstreamBridge::Stream'
34
+ )
35
+ end
36
+
37
+ def log_all_blocked(name, blocked)
38
+ if blocked.any?
39
+ Logging.warn(
40
+ "Stream #{name}: all missing subjects belong to other streams; unchanged. " \
41
+ "blocked=#{blocked.inspect}",
42
+ tag: 'JetstreamBridge::Stream'
43
+ )
44
+ else
45
+ Logging.info("Stream #{name} exists; nothing to add.", tag: 'JetstreamBridge::Stream')
46
+ end
47
+ end
48
+
49
+ def log_updated(name, added, blocked)
50
+ msg = "Updated stream #{name}; added subjects=#{added.inspect}"
51
+ msg += " (skipped overlapped=#{blocked.inspect})" if blocked.any?
52
+ Logging.info(msg, tag: 'JetstreamBridge::Stream')
53
+ end
54
+
55
+ def log_not_created(name, blocked)
56
+ Logging.warn(
57
+ "Not creating stream #{name}: all desired subjects belong to other streams. " \
58
+ "blocked=#{blocked.inspect}",
59
+ tag: 'JetstreamBridge::Stream'
60
+ )
61
+ end
62
+
63
+ def log_created(name, allowed, blocked, retention, storage)
64
+ msg = [
65
+ "Created stream #{name}",
66
+ "subjects=#{allowed.inspect}",
67
+ "retention=#{retention.inspect}",
68
+ "storage=#{storage.inspect}"
69
+ ].join(' ')
70
+ msg += " (skipped overlapped=#{blocked.inspect})" if blocked.any?
71
+ Logging.info(msg, tag: 'JetstreamBridge::Stream')
72
+ end
73
+ end
74
+
75
+ # Ensures a stream exists and updates only uncovered subjects, using work-queue semantics:
76
+ # Persist with zero consumers; delete after the first ack (retention: 'workqueue').
9
77
  class Stream
78
+ RETENTION = 'workqueue'
79
+ STORAGE = 'file'
80
+
10
81
  class << self
11
82
  def ensure!(jts, name, subjects)
12
- desired = normalize_subjects(subjects)
83
+ desired = StreamSupport.normalize_subjects(subjects)
13
84
  raise ArgumentError, 'subjects must not be empty' if desired.empty?
14
85
 
15
86
  attempts = 0
@@ -17,14 +88,18 @@ module JetstreamBridge
17
88
  info = safe_stream_info(jts, name)
18
89
  info ? ensure_update(jts, name, info, desired) : ensure_create(jts, name, desired)
19
90
  rescue NATS::JetStream::Error => e
20
- if overlap_error?(e) && (attempts += 1) <= 1
21
- Logging.warn("Overlap race while ensuring #{name}; retrying once...", tag: 'JetstreamBridge::Stream')
91
+ if StreamSupport.overlap_error?(e) && (attempts += 1) <= 1
92
+ Logging.warn(
93
+ "Overlap race while ensuring #{name}; retrying once...",
94
+ tag: 'JetstreamBridge::Stream'
95
+ )
22
96
  sleep(0.05)
23
97
  retry
24
- elsif overlap_error?(e)
98
+ elsif StreamSupport.overlap_error?(e)
25
99
  Logging.warn(
26
100
  "Overlap persists ensuring #{name}; leaving unchanged. err=#{e.message.inspect}",
27
- tag: 'JetstreamBridge::Stream')
101
+ tag: 'JetstreamBridge::Stream'
102
+ )
28
103
  nil
29
104
  else
30
105
  raise
@@ -34,95 +109,77 @@ module JetstreamBridge
34
109
 
35
110
  private
36
111
 
37
- # ---------- Update existing stream ----------
38
-
39
- def ensure_update(jts, name, info, desired)
40
- existing = normalize_subjects(info.config.subjects || [])
41
- to_add = missing_subjects(existing, desired)
42
- return log_already_covered(name) if to_add.empty?
43
-
44
- allowed, blocked = OverlapGuard.partition_allowed(jts, name, to_add)
45
- return log_all_blocked(name, blocked) if allowed.empty?
46
-
47
- target = (existing + allowed).uniq
48
- OverlapGuard.check!(jts, name, target)
49
- jts.update_stream(name: name, subjects: target)
50
- log_updated(name, allowed, blocked)
51
- end
112
+ # ---- keep ensure_update small (<=20 lines, lower ABC) ----
113
+ def ensure_update(jts, name, info, desired_subjects)
114
+ existing = StreamSupport.normalize_subjects(info.config.subjects || [])
115
+ to_add = StreamSupport.missing_subjects(existing, desired_subjects)
52
116
 
53
- # ---------- Create new stream ----------
117
+ return add_subjects(jts, name, existing, to_add) if to_add.any?
54
118
 
55
- def ensure_create(jts, name, desired)
56
- allowed, blocked = OverlapGuard.partition_allowed(jts, name, desired)
57
- return log_not_created(name, blocked) if allowed.empty?
119
+ if config_needs_update?(info)
120
+ apply_update(jts, name, existing)
121
+ return log_config_updated(name)
122
+ end
58
123
 
59
- jts.add_stream(name: name, subjects: allowed, retention: 'interest', storage: 'file')
60
- log_created(name, allowed, blocked)
124
+ StreamSupport.log_already_covered(name)
61
125
  end
62
126
 
63
- # ---------- Helpers ----------
64
-
65
- def safe_stream_info(jts, name)
66
- jts.stream_info(name)
67
- rescue NATS::JetStream::Error => e
68
- return nil if stream_not_found?(e)
69
- raise
70
- end
71
-
72
- def missing_subjects(existing, desired)
73
- desired.reject { |d| SubjectMatcher.covered?(existing, d) }
74
- end
127
+ # ---- tiny helpers extracted to reduce ABC ----
128
+ def add_subjects(jts, name, existing, to_add)
129
+ allowed, blocked = OverlapGuard.partition_allowed(jts, name, to_add)
130
+ return StreamSupport.log_all_blocked(name, blocked) if allowed.empty?
75
131
 
76
- def normalize_subjects(list)
77
- Array(list).flatten.compact.map!(&:to_s).reject(&:empty?).uniq
132
+ target = merge_subjects(existing, allowed)
133
+ OverlapGuard.check!(jts, name, target)
134
+ apply_update(jts, name, target)
135
+ StreamSupport.log_updated(name, allowed, blocked)
78
136
  end
79
137
 
80
- def stream_not_found?(error)
81
- msg = error.message.to_s
82
- msg =~ /stream\s+not\s+found/i || msg =~ /\b404\b/
138
+ def merge_subjects(existing, allowed)
139
+ (existing + allowed).uniq
83
140
  end
84
141
 
85
- def overlap_error?(error)
86
- msg = error.message.to_s
87
- msg =~ /subjects?\s+overlap/i || msg =~ /\berr_code=10065\b/ || msg =~ /\bstatus_code=400\b/
142
+ def config_needs_update?(info)
143
+ info.config.retention.to_s.downcase != RETENTION ||
144
+ info.config.storage.to_s.downcase != STORAGE
88
145
  end
89
146
 
90
- # ---------- Logging wrappers ----------
91
-
92
- def log_already_covered(name)
93
- Logging.info("Stream #{name} exists; subjects already covered.", tag: 'JetstreamBridge::Stream')
147
+ def apply_update(jts, name, subjects)
148
+ jts.update_stream(
149
+ name: name,
150
+ subjects: subjects,
151
+ retention: RETENTION,
152
+ storage: STORAGE
153
+ )
94
154
  end
95
155
 
96
- def log_all_blocked(name, blocked)
97
- if blocked.any?
98
- Logging.warn(
99
- "Stream #{name}: all missing subjects are owned by other streams; leaving unchanged. " \
100
- "blocked=#{blocked.inspect}",
101
- tag: 'JetstreamBridge::Stream'
102
- )
103
- else
104
- Logging.info("Stream #{name} exists; nothing to add.", tag: 'JetstreamBridge::Stream')
105
- end
156
+ def log_config_updated(name)
157
+ Logging.info(
158
+ "Updated stream #{name} config; retention=#{RETENTION.inspect} " \
159
+ "storage=#{STORAGE.inspect}",
160
+ tag: 'JetstreamBridge::Stream'
161
+ )
106
162
  end
107
163
 
108
- def log_updated(name, added, blocked)
109
- msg = "Updated stream #{name}; added subjects=#{added.inspect}"
110
- msg += " (skipped overlapped=#{blocked.inspect})" if blocked.any?
111
- Logging.info(msg, tag: 'JetstreamBridge::Stream')
112
- end
164
+ def ensure_create(jts, name, desired_subjects)
165
+ allowed, blocked = OverlapGuard.partition_allowed(jts, name, desired_subjects)
166
+ return StreamSupport.log_not_created(name, blocked) if allowed.empty?
113
167
 
114
- def log_not_created(name, blocked)
115
- Logging.warn(
116
- "Not creating stream #{name}: all desired subjects are owned by other streams. " \
117
- "blocked=#{blocked.inspect}",
118
- tag: 'JetstreamBridge::Stream'
168
+ jts.add_stream(
169
+ name: name,
170
+ subjects: allowed,
171
+ retention: RETENTION,
172
+ storage: STORAGE
119
173
  )
174
+ StreamSupport.log_created(name, allowed, blocked, RETENTION, STORAGE)
120
175
  end
121
176
 
122
- def log_created(name, allowed, blocked)
123
- msg = "Created stream #{name} subjects=#{allowed.inspect}"
124
- msg += " (skipped overlapped=#{blocked.inspect})" if blocked.any?
125
- Logging.info(msg, tag: 'JetstreamBridge::Stream')
177
+ def safe_stream_info(jts, name)
178
+ jts.stream_info(name)
179
+ rescue NATS::JetStream::Error => e
180
+ return nil if StreamSupport.stream_not_found?(e)
181
+
182
+ raise
126
183
  end
127
184
  end
128
185
  end
@@ -4,5 +4,5 @@
4
4
  #
5
5
  # Version constant for the gem.
6
6
  module JetstreamBridge
7
- VERSION = '2.2.1'
7
+ VERSION = '2.4.0'
8
8
  end
@@ -46,7 +46,7 @@ module JetstreamBridge
46
46
  config.use_dlq
47
47
  end
48
48
 
49
- def ensure_topology!
49
+ def ensure_topology?
50
50
  Connection.connect!
51
51
  true
52
52
  end
metadata CHANGED
@@ -1,17 +1,17 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jetstream_bridge
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.2.1
4
+ version: 2.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mike Attara
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-08-20 00:00:00.000000000 Z
11
+ date: 2025-08-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: activerecord
14
+ name: activesupport
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - ">="
@@ -25,7 +25,7 @@ dependencies:
25
25
  - !ruby/object:Gem::Version
26
26
  version: '6.0'
27
27
  - !ruby/object:Gem::Dependency
28
- name: activesupport
28
+ name: activerecord
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - ">="
@@ -53,33 +53,19 @@ dependencies:
53
53
  - !ruby/object:Gem::Version
54
54
  version: '2.4'
55
55
  - !ruby/object:Gem::Dependency
56
- name: rails
56
+ name: oj
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - ">="
60
60
  - !ruby/object:Gem::Version
61
- version: '6.0'
61
+ version: '3.16'
62
62
  type: :runtime
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - ">="
67
67
  - !ruby/object:Gem::Version
68
- version: '6.0'
69
- - !ruby/object:Gem::Dependency
70
- name: bundler-audit
71
- requirement: !ruby/object:Gem::Requirement
72
- requirements:
73
- - - ">="
74
- - !ruby/object:Gem::Version
75
- version: 0.9.1
76
- type: :development
77
- prerelease: false
78
- version_requirements: !ruby/object:Gem::Requirement
79
- requirements:
80
- - - ">="
81
- - !ruby/object:Gem::Version
82
- version: 0.9.1
68
+ version: '3.16'
83
69
  - !ruby/object:Gem::Dependency
84
70
  name: rake
85
71
  requirement: !ruby/object:Gem::Requirement
@@ -150,6 +136,20 @@ dependencies:
150
136
  - - "~>"
151
137
  - !ruby/object:Gem::Version
152
138
  version: '1.21'
139
+ - !ruby/object:Gem::Dependency
140
+ name: bundler-audit
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: 0.9.1
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: 0.9.1
153
153
  description: |-
154
154
  Publisher/Consumer utilities for NATS JetStream with environment-scoped subjects,
155
155
  overlap guards, DLQ routing, retries/backoff, and optional Inbox/Outbox patterns.
@@ -160,20 +160,6 @@ executables: []
160
160
  extensions: []
161
161
  extra_rdoc_files: []
162
162
  files:
163
- - ".github/workflows/release.yml"
164
- - ".gitignore"
165
- - ".idea/.gitignore"
166
- - ".idea/dictionaries/project.xml"
167
- - ".idea/jetstream_bridge.iml"
168
- - ".idea/misc.xml"
169
- - ".idea/modules.xml"
170
- - ".idea/vcs.xml"
171
- - ".rubocop.yml"
172
- - Gemfile
173
- - Gemfile.lock
174
- - LICENSE
175
- - README.md
176
- - jetstream_bridge.gemspec
177
163
  - lib/generators/jetstream_bridge/initializer/initializer_generator.rb
178
164
  - lib/generators/jetstream_bridge/initializer/templates/jetstream_bridge.rb
179
165
  - lib/generators/jetstream_bridge/install/install_generator.rb
@@ -181,11 +167,14 @@ files:
181
167
  - lib/generators/jetstream_bridge/migrations/templates/create_jetstream_inbox_events.rb.erb
182
168
  - lib/generators/jetstream_bridge/migrations/templates/create_jetstream_outbox_events.rb.erb
183
169
  - lib/jetstream_bridge.rb
170
+ - lib/jetstream_bridge/consumer/backoff_strategy.rb
184
171
  - lib/jetstream_bridge/consumer/consumer.rb
185
172
  - lib/jetstream_bridge/consumer/consumer_config.rb
173
+ - lib/jetstream_bridge/consumer/dlq_publisher.rb
186
174
  - lib/jetstream_bridge/consumer/inbox/inbox_message.rb
187
175
  - lib/jetstream_bridge/consumer/inbox/inbox_processor.rb
188
176
  - lib/jetstream_bridge/consumer/inbox/inbox_repository.rb
177
+ - lib/jetstream_bridge/consumer/message_context.rb
189
178
  - lib/jetstream_bridge/consumer/message_processor.rb
190
179
  - lib/jetstream_bridge/consumer/subscription_manager.rb
191
180
  - lib/jetstream_bridge/core/config.rb