cdc-core 0.0.0 → 0.1.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.
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CDC
4
+ module Core
5
+ # Canonical CDC operation names.
6
+ #
7
+ # Operations are represented as symbols to keep event objects small,
8
+ # immutable, and easy to compare. Use .normalize when accepting user input
9
+ # and .supported? when validating optional values.
10
+ module Operation
11
+ # Insert row operation.
12
+ INSERT = :insert
13
+ # Update row operation.
14
+ UPDATE = :update
15
+ # Delete row operation.
16
+ DELETE = :delete
17
+ # Truncate table operation.
18
+ TRUNCATE = :truncate
19
+ # Logical replication message operation.
20
+ MESSAGE = :message
21
+ # All operation names currently recognized by cdc-core.
22
+ SUPPORTED = Ractor.make_shareable([INSERT, UPDATE, DELETE, TRUNCATE, MESSAGE].freeze)
23
+ # Minimal row-change operations used by the initial runtime surface.
24
+ MVP = Ractor.make_shareable([INSERT, UPDATE, DELETE].freeze)
25
+
26
+ module_function
27
+
28
+ # Convert an operation-like value into a supported operation symbol.
29
+ #
30
+ # @param operation [#to_sym] value to normalize
31
+ # @return [Symbol] one of SUPPORTED
32
+ # @raise [InvalidOperationError] when the operation is not supported
33
+ def normalize(operation)
34
+ value = operation.to_sym
35
+ return value if SUPPORTED.include?(value)
36
+
37
+ raise InvalidOperationError, "unsupported CDC operation: #{operation.inspect}"
38
+ end
39
+
40
+ # Check whether an operation-like value is supported.
41
+ #
42
+ # @param operation [#to_sym] value to check
43
+ # @return [Boolean] true when the value normalizes to a supported operation
44
+ def supported?(operation)
45
+ SUPPORTED.include?(operation.to_sym)
46
+ rescue NoMethodError
47
+ false
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CDC
4
+ module Core
5
+ # Connects filters with a processor to form an event-processing unit.
6
+ #
7
+ # A Pipeline first evaluates all filters. Matching events are handed to the
8
+ # processor, while filtered events produce skipped results. Processor errors
9
+ # are captured as failure results instead of escaping to the caller.
10
+ class Pipeline
11
+ # @return [#process] processor invoked for matching events
12
+ # @return [Array<Filter>] filters that must all match before processing
13
+ attr_reader :processor, :filters
14
+
15
+ # Build a pipeline.
16
+ #
17
+ # @param processor [#process] processor for matching events
18
+ # @param filters [Array<Filter>] filters applied before processing
19
+ def initialize(processor:, filters: [])
20
+ @processor = processor
21
+ @filters = filters.freeze
22
+ end
23
+
24
+ # Process one event through the pipeline.
25
+ #
26
+ # @param event [ChangeEvent] event to process
27
+ # @return [ProcessorResult]
28
+ def process(event)
29
+ return ProcessorResult.skipped(event, metadata: { reason: 'filtered' }) unless matches?(event)
30
+
31
+ normalize_result(processor.process(event), event)
32
+ rescue StandardError => e
33
+ ProcessorResult.failure(e, event:)
34
+ end
35
+
36
+ # Process many events in order.
37
+ #
38
+ # @param events [Enumerable<ChangeEvent>] events to process
39
+ # @return [Array<ProcessorResult>]
40
+ def process_many(events)
41
+ events.map { |event| process(event) }.freeze
42
+ end
43
+
44
+ private
45
+
46
+ # Check whether every filter matches an event.
47
+ #
48
+ # @param event [ChangeEvent]
49
+ # @return [Boolean]
50
+ def matches?(event)
51
+ filters.all? { |filter| filter.match?(event) }
52
+ end
53
+
54
+ # Normalize raw processor output into a ProcessorResult.
55
+ #
56
+ # @param result [Object] raw processor result
57
+ # @param event [ChangeEvent] processed event
58
+ # @return [ProcessorResult]
59
+ def normalize_result(result, event)
60
+ return result if result.is_a?(ProcessorResult)
61
+
62
+ result ? ProcessorResult.success(event) : ProcessorResult.skipped(event)
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CDC
4
+ module Core
5
+ # Base class for CDC event processors.
6
+ #
7
+ # Subclasses implement #process and may opt into Ractor-safe execution by
8
+ # calling .ractor_safe!. cdc-core itself does not schedule Ractors; it only
9
+ # records processor capabilities for runtime layers such as cdc-ractor.
10
+ class Processor
11
+ # Mark this processor class as safe to execute in Ractor-aware runtimes.
12
+ #
13
+ # @return [true]
14
+ def self.ractor_safe!
15
+ @ractor_safe = true
16
+ end
17
+
18
+ # Whether this processor class has declared Ractor safety.
19
+ #
20
+ # @return [Boolean]
21
+ def self.ractor_safe?
22
+ @ractor_safe == true
23
+ end
24
+
25
+ # Whether this processor instance is Ractor-safe.
26
+ #
27
+ # @return [Boolean]
28
+ def ractor_safe?
29
+ self.class.ractor_safe?
30
+ end
31
+
32
+ # Process one event.
33
+ #
34
+ # Subclasses must override this method.
35
+ #
36
+ # @param _event [ChangeEvent] event to process
37
+ # @raise [NotImplementedError] when not implemented by a subclass
38
+ def process(_event)
39
+ raise NotImplementedError, "#{self.class} must implement #process"
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CDC
4
+ module Core
5
+ # Result returned by processors and pipelines.
6
+ #
7
+ # ProcessorResult standardizes processor outcomes so callers can distinguish
8
+ # successful processing, skipped events, and failures without relying on
9
+ # processor-specific return values.
10
+ class ProcessorResult
11
+ # @return [Symbol] result status
12
+ # @return [ChangeEvent, nil] event associated with the result
13
+ # @return [Exception, nil] failure error, when status is :failure
14
+ # @return [EventMetadata] result metadata
15
+ attr_reader :status, :event, :error, :metadata
16
+
17
+ # Build a successful result.
18
+ #
19
+ # @param event [ChangeEvent, nil] processed event
20
+ # @param metadata [Hash, EventMetadata] result metadata
21
+ # @return [ProcessorResult]
22
+ def self.success(event = nil, metadata: {}) = new(:success, event:, metadata:)
23
+
24
+ # Build a failure result.
25
+ #
26
+ # @param error [Exception] processor error
27
+ # @param event [ChangeEvent, nil] event being processed
28
+ # @param metadata [Hash, EventMetadata] result metadata
29
+ # @return [ProcessorResult]
30
+ def self.failure(error, event: nil, metadata: {}) = new(:failure, event:, error:, metadata:)
31
+
32
+ # Build a skipped result.
33
+ #
34
+ # @param event [ChangeEvent, nil] skipped event
35
+ # @param metadata [Hash, EventMetadata] result metadata
36
+ # @return [ProcessorResult]
37
+ def self.skipped(event = nil, metadata: {}) = new(:skipped, event:, metadata:)
38
+
39
+ # Build a processor result with an explicit status.
40
+ #
41
+ # @param status [#to_sym] result status
42
+ # @param event [ChangeEvent, nil] associated event
43
+ # @param error [Exception, nil] associated failure
44
+ # @param metadata [Hash, EventMetadata] result metadata
45
+ def initialize(status, event: nil, error: nil, metadata: {})
46
+ @status = status.to_sym
47
+ @event = event
48
+ @error = error
49
+ @metadata = metadata.is_a?(EventMetadata) ? metadata : EventMetadata.new(metadata)
50
+ Ractor.make_shareable(self) unless error
51
+ end
52
+
53
+ # @return [Boolean] true when status is :success
54
+ def success? = status == :success
55
+
56
+ # @return [Boolean] true when status is :failure
57
+ def failure? = status == :failure
58
+
59
+ # @return [Boolean] true when status is :skipped
60
+ def skipped? = status == :skipped
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CDC
4
+ module Core
5
+ # Immutable group of change events committed in one transaction.
6
+ #
7
+ # TransactionEnvelope is useful when a downstream processor needs transaction
8
+ # boundaries instead of isolated row-level events. The contained events and
9
+ # metadata are Ractor-shareable when construction succeeds.
10
+ class TransactionEnvelope
11
+ # @return [Object] transaction identifier
12
+ # @return [Array<ChangeEvent>] events committed by the transaction
13
+ # @return [String, nil] commit log sequence number
14
+ # @return [Time, nil] commit timestamp
15
+ # @return [EventMetadata] transaction metadata
16
+ attr_reader :transaction_id, :events, :commit_lsn, :committed_at, :metadata
17
+
18
+ # Build a transaction envelope.
19
+ #
20
+ # @param transaction_id [Object] upstream transaction identifier
21
+ # @param events [Array<ChangeEvent>] committed events
22
+ # @param commit_lsn [#to_s, nil] commit log sequence number
23
+ # @param committed_at [Time, nil] commit timestamp
24
+ # @param metadata [Hash, EventMetadata] transaction metadata
25
+ def initialize(transaction_id:, events:, commit_lsn: nil, committed_at: nil, metadata: {})
26
+ @transaction_id = transaction_id
27
+ @events = Ractor.make_shareable(events.freeze)
28
+ @commit_lsn = commit_lsn&.to_s&.freeze
29
+ @committed_at = committed_at
30
+ @metadata = metadata.is_a?(EventMetadata) ? metadata : EventMetadata.new(metadata)
31
+ Ractor.make_shareable(self)
32
+ end
33
+
34
+ # Whether the envelope has no events.
35
+ #
36
+ # @return [Boolean]
37
+ def empty? = events.empty?
38
+
39
+ # Number of events in the envelope.
40
+ #
41
+ # @return [Integer]
42
+ def size = events.size
43
+
44
+ # Convert the transaction envelope into a Ractor-shareable hash.
45
+ #
46
+ # @return [Hash{String=>Object,nil}]
47
+ def to_h
48
+ Ractor.make_shareable({
49
+ 'transaction_id' => transaction_id,
50
+ 'events' => events.map(&:to_h).freeze,
51
+ 'commit_lsn' => commit_lsn,
52
+ 'committed_at' => committed_at,
53
+ 'metadata' => metadata.to_h
54
+ }.freeze)
55
+ end
56
+ end
57
+ end
58
+ end
@@ -1,7 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Cdc
3
+ module CDC
4
4
  module Core
5
- VERSION = "0.0.0"
5
+ # Current gem version.
6
+ VERSION = '0.1.0'
6
7
  end
7
8
  end
data/lib/cdc/core.rb CHANGED
@@ -1,10 +1,26 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "core/version"
3
+ require_relative 'core/version'
4
+ require_relative 'core/errors'
5
+ require_relative 'core/operation'
6
+ require_relative 'core/column_change'
7
+ require_relative 'core/event_metadata'
8
+ require_relative 'core/change_event'
9
+ require_relative 'core/transaction_envelope'
10
+ require_relative 'core/processor_result'
11
+ require_relative 'core/processor'
12
+ require_relative 'core/composite_processor'
13
+ require_relative 'core/filter'
14
+ require_relative 'core/pipeline'
4
15
 
5
- module Cdc
16
+ # Top-level namespace for Change Data Capture libraries.
17
+ module CDC
18
+ # Database-agnostic Change Data Capture domain primitives.
19
+ #
20
+ # CDC::Core intentionally contains only lightweight runtime abstractions:
21
+ # events, metadata, processors, filters, pipelines, and processor results.
22
+ # Transport, PostgreSQL protocol parsing, and value decoding live in sibling
23
+ # gems so this layer can remain independently useful.
6
24
  module Core
7
- class Error < StandardError; end
8
- # Your code goes here...
9
25
  end
10
26
  end
data/lib/cdc_core.rb ADDED
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Main entry point for the cdc-core gem.
4
+ #
5
+ # Requiring this file loads the public CDC::Core namespace and all runtime
6
+ # primitives used to model, filter, and process change data capture events.
7
+ require_relative 'cdc/core'
@@ -0,0 +1,226 @@
1
+ module CDC
2
+ module Core
3
+ # Immutable representation of one logical database change.
4
+ #
5
+ # ChangeEvent is the core data structure passed through filters, pipelines,
6
+ # and processors. It is database-agnostic but carries common CDC fields such
7
+ # as operation, schema, table, before/after values, primary key, LSN, and
8
+ # metadata.
9
+ class ChangeEvent
10
+ @operation: untyped
11
+
12
+ @schema: untyped
13
+
14
+ @table: untyped
15
+
16
+ @old_values: untyped
17
+
18
+ @new_values: untyped
19
+
20
+ @primary_key: untyped
21
+
22
+ @transaction_id: untyped
23
+
24
+ @commit_lsn: untyped
25
+
26
+ @sequence_number: untyped
27
+
28
+ @occurred_at: untyped
29
+
30
+ @metadata: untyped
31
+
32
+ # @return [Symbol] normalized CDC operation
33
+ # @return [String] database schema name
34
+ # @return [String] database table name
35
+ # @return [Hash, nil] values before the change
36
+ # @return [Hash, nil] values after the change
37
+ # @return [Hash, nil] primary key values for the changed row
38
+ # @return [Object, nil] transaction identifier from the upstream source
39
+ # @return [String, nil] commit log sequence number
40
+ # @return [Integer, nil] event sequence within a transaction or stream
41
+ # @return [Time, nil] timestamp associated with the event
42
+ # @return [EventMetadata] additional normalized metadata
43
+ attr_reader operation: untyped
44
+
45
+ # @return [Symbol] normalized CDC operation
46
+ # @return [String] database schema name
47
+ # @return [String] database table name
48
+ # @return [Hash, nil] values before the change
49
+ # @return [Hash, nil] values after the change
50
+ # @return [Hash, nil] primary key values for the changed row
51
+ # @return [Object, nil] transaction identifier from the upstream source
52
+ # @return [String, nil] commit log sequence number
53
+ # @return [Integer, nil] event sequence within a transaction or stream
54
+ # @return [Time, nil] timestamp associated with the event
55
+ # @return [EventMetadata] additional normalized metadata
56
+ attr_reader schema: untyped
57
+
58
+ # @return [Symbol] normalized CDC operation
59
+ # @return [String] database schema name
60
+ # @return [String] database table name
61
+ # @return [Hash, nil] values before the change
62
+ # @return [Hash, nil] values after the change
63
+ # @return [Hash, nil] primary key values for the changed row
64
+ # @return [Object, nil] transaction identifier from the upstream source
65
+ # @return [String, nil] commit log sequence number
66
+ # @return [Integer, nil] event sequence within a transaction or stream
67
+ # @return [Time, nil] timestamp associated with the event
68
+ # @return [EventMetadata] additional normalized metadata
69
+ attr_reader table: untyped
70
+
71
+ # @return [Symbol] normalized CDC operation
72
+ # @return [String] database schema name
73
+ # @return [String] database table name
74
+ # @return [Hash, nil] values before the change
75
+ # @return [Hash, nil] values after the change
76
+ # @return [Hash, nil] primary key values for the changed row
77
+ # @return [Object, nil] transaction identifier from the upstream source
78
+ # @return [String, nil] commit log sequence number
79
+ # @return [Integer, nil] event sequence within a transaction or stream
80
+ # @return [Time, nil] timestamp associated with the event
81
+ # @return [EventMetadata] additional normalized metadata
82
+ attr_reader old_values: untyped
83
+
84
+ # @return [Symbol] normalized CDC operation
85
+ # @return [String] database schema name
86
+ # @return [String] database table name
87
+ # @return [Hash, nil] values before the change
88
+ # @return [Hash, nil] values after the change
89
+ # @return [Hash, nil] primary key values for the changed row
90
+ # @return [Object, nil] transaction identifier from the upstream source
91
+ # @return [String, nil] commit log sequence number
92
+ # @return [Integer, nil] event sequence within a transaction or stream
93
+ # @return [Time, nil] timestamp associated with the event
94
+ # @return [EventMetadata] additional normalized metadata
95
+ attr_reader new_values: untyped
96
+
97
+ # @return [Symbol] normalized CDC operation
98
+ # @return [String] database schema name
99
+ # @return [String] database table name
100
+ # @return [Hash, nil] values before the change
101
+ # @return [Hash, nil] values after the change
102
+ # @return [Hash, nil] primary key values for the changed row
103
+ # @return [Object, nil] transaction identifier from the upstream source
104
+ # @return [String, nil] commit log sequence number
105
+ # @return [Integer, nil] event sequence within a transaction or stream
106
+ # @return [Time, nil] timestamp associated with the event
107
+ # @return [EventMetadata] additional normalized metadata
108
+ attr_reader primary_key: untyped
109
+
110
+ # @return [Symbol] normalized CDC operation
111
+ # @return [String] database schema name
112
+ # @return [String] database table name
113
+ # @return [Hash, nil] values before the change
114
+ # @return [Hash, nil] values after the change
115
+ # @return [Hash, nil] primary key values for the changed row
116
+ # @return [Object, nil] transaction identifier from the upstream source
117
+ # @return [String, nil] commit log sequence number
118
+ # @return [Integer, nil] event sequence within a transaction or stream
119
+ # @return [Time, nil] timestamp associated with the event
120
+ # @return [EventMetadata] additional normalized metadata
121
+ attr_reader transaction_id: untyped
122
+
123
+ # @return [Symbol] normalized CDC operation
124
+ # @return [String] database schema name
125
+ # @return [String] database table name
126
+ # @return [Hash, nil] values before the change
127
+ # @return [Hash, nil] values after the change
128
+ # @return [Hash, nil] primary key values for the changed row
129
+ # @return [Object, nil] transaction identifier from the upstream source
130
+ # @return [String, nil] commit log sequence number
131
+ # @return [Integer, nil] event sequence within a transaction or stream
132
+ # @return [Time, nil] timestamp associated with the event
133
+ # @return [EventMetadata] additional normalized metadata
134
+ attr_reader commit_lsn: untyped
135
+
136
+ # @return [Symbol] normalized CDC operation
137
+ # @return [String] database schema name
138
+ # @return [String] database table name
139
+ # @return [Hash, nil] values before the change
140
+ # @return [Hash, nil] values after the change
141
+ # @return [Hash, nil] primary key values for the changed row
142
+ # @return [Object, nil] transaction identifier from the upstream source
143
+ # @return [String, nil] commit log sequence number
144
+ # @return [Integer, nil] event sequence within a transaction or stream
145
+ # @return [Time, nil] timestamp associated with the event
146
+ # @return [EventMetadata] additional normalized metadata
147
+ attr_reader sequence_number: untyped
148
+
149
+ # @return [Symbol] normalized CDC operation
150
+ # @return [String] database schema name
151
+ # @return [String] database table name
152
+ # @return [Hash, nil] values before the change
153
+ # @return [Hash, nil] values after the change
154
+ # @return [Hash, nil] primary key values for the changed row
155
+ # @return [Object, nil] transaction identifier from the upstream source
156
+ # @return [String, nil] commit log sequence number
157
+ # @return [Integer, nil] event sequence within a transaction or stream
158
+ # @return [Time, nil] timestamp associated with the event
159
+ # @return [EventMetadata] additional normalized metadata
160
+ attr_reader occurred_at: untyped
161
+
162
+ # @return [Symbol] normalized CDC operation
163
+ # @return [String] database schema name
164
+ # @return [String] database table name
165
+ # @return [Hash, nil] values before the change
166
+ # @return [Hash, nil] values after the change
167
+ # @return [Hash, nil] primary key values for the changed row
168
+ # @return [Object, nil] transaction identifier from the upstream source
169
+ # @return [String, nil] commit log sequence number
170
+ # @return [Integer, nil] event sequence within a transaction or stream
171
+ # @return [Time, nil] timestamp associated with the event
172
+ # @return [EventMetadata] additional normalized metadata
173
+ attr_reader metadata: untyped
174
+
175
+ # Build a change event.
176
+ #
177
+ # @param operation [#to_sym] CDC operation
178
+ # @param schema [#to_s] schema name
179
+ # @param table [#to_s] table name
180
+ # @param old_values [Hash, nil] values before the change
181
+ # @param new_values [Hash, nil] values after the change
182
+ # @param primary_key [Hash, nil] primary key values
183
+ # @param transaction_id [Object, nil] source transaction identifier
184
+ # @param commit_lsn [#to_s, nil] commit log sequence number
185
+ # @param sequence_number [Integer, nil] event sequence number
186
+ # @param occurred_at [Time, nil] event timestamp
187
+ # @param metadata [Hash, EventMetadata] additional event metadata
188
+ def initialize: (operation: untyped, schema: untyped, table: untyped, ?old_values: untyped?, ?new_values: untyped?, ?primary_key: untyped?, ?transaction_id: untyped?, ?commit_lsn: untyped?, ?sequence_number: untyped?, ?occurred_at: untyped?, ?metadata: ::Hash[untyped, untyped]) -> void
189
+
190
+ # @return [Boolean] true for insert events
191
+ def insert?: () -> untyped
192
+
193
+ # @return [Boolean] true for update events
194
+ def update?: () -> untyped
195
+
196
+ # @return [Boolean] true for delete events
197
+ def delete?: () -> untyped
198
+
199
+ # Fully qualified table name in schema.table form.
200
+ #
201
+ # @return [String]
202
+ def qualified_table_name: () -> ::String
203
+
204
+ # Compute changed columns by comparing old and new values.
205
+ #
206
+ # Columns with equal old and new values are omitted. Insert and delete
207
+ # events can pass nil for one side; missing values are represented as nil.
208
+ #
209
+ # @return [Array<ColumnChange>] Ractor-shareable changed columns
210
+ def changes: () -> untyped
211
+
212
+ # Convert the event into a Ractor-shareable hash.
213
+ #
214
+ # @return [Hash{String=>Object,nil}]
215
+ def to_h: () -> untyped
216
+
217
+ private
218
+
219
+ # Convert a hash into immutable EventMetadata storage, preserving nil.
220
+ #
221
+ # @param hash [Hash, nil]
222
+ # @return [Hash, nil]
223
+ def freeze_hash_or_nil: (untyped hash) -> (nil | untyped)
224
+ end
225
+ end
226
+ end
@@ -0,0 +1,56 @@
1
+ module CDC
2
+ module Core
3
+ # Represents a single column-level value change.
4
+ #
5
+ # ColumnChange is immutable and Ractor-shareable. Values that cannot be made
6
+ # shareable by Ruby are represented by their frozen #inspect string so the
7
+ # enclosing event can still cross Ractor boundaries safely.
8
+ class ColumnChange
9
+ @name: untyped
10
+
11
+ @old_value: untyped
12
+
13
+ @new_value: untyped
14
+
15
+ # @return [String] column name
16
+ # @return [Object, nil] value before the change
17
+ # @return [Object, nil] value after the change
18
+ attr_reader name: untyped
19
+
20
+ # @return [String] column name
21
+ # @return [Object, nil] value before the change
22
+ # @return [Object, nil] value after the change
23
+ attr_reader old_value: untyped
24
+
25
+ # @return [String] column name
26
+ # @return [Object, nil] value before the change
27
+ # @return [Object, nil] value after the change
28
+ attr_reader new_value: untyped
29
+
30
+ # Build a column-level change object.
31
+ #
32
+ # @param name [#to_s] column name
33
+ # @param old_value [Object, nil] previous value
34
+ # @param new_value [Object, nil] new value
35
+ def initialize: (name: untyped, old_value: untyped, new_value: untyped) -> void
36
+
37
+ # Whether the old and new values differ.
38
+ #
39
+ # @return [Boolean]
40
+ def changed?: () -> untyped
41
+
42
+ # Convert the change into a Ractor-shareable hash.
43
+ #
44
+ # @return [Hash{String=>Object,nil}]
45
+ def to_h: () -> untyped
46
+
47
+ private
48
+
49
+ # Convert a value into a Ractor-shareable representation.
50
+ #
51
+ # @param value [Object, nil]
52
+ # @return [Object, String, nil]
53
+ def make_value_shareable: (untyped value) -> untyped
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,53 @@
1
+ module CDC
2
+ module Core
3
+ # Processor that delegates each event to multiple processors.
4
+ #
5
+ # CompositeProcessor enables fan-out processing while preserving a simple
6
+ # sequential execution model. It normalizes truthy/falsey processor returns
7
+ # into ProcessorResult objects and can stop at the first failure.
8
+ class CompositeProcessor < Processor
9
+ @processors: untyped
10
+
11
+ @fail_fast: untyped
12
+
13
+ # @return [Array<Processor>] processors executed for each event
14
+ # @return [Boolean] whether processing stops on the first failure
15
+ attr_reader processors: untyped
16
+
17
+ # @return [Array<Processor>] processors executed for each event
18
+ # @return [Boolean] whether processing stops on the first failure
19
+ attr_reader fail_fast: untyped
20
+
21
+ # Build a composite processor.
22
+ #
23
+ # @param processors [Array<#process>] processors to execute
24
+ # @param fail_fast [Boolean] whether to stop after the first failure
25
+ def initialize: (untyped processors, ?fail_fast: bool) -> void
26
+
27
+ # Process an event through each configured processor.
28
+ #
29
+ # @param event [ChangeEvent] event to process
30
+ # @return [Array<ProcessorResult>] result from each attempted processor
31
+ def process: (untyped event) -> untyped
32
+
33
+ # Processors that declared Ractor safety.
34
+ #
35
+ # @return [Array<Processor>]
36
+ def ractor_safe_processors: () -> untyped
37
+
38
+ # Processors that should remain sequential in the core runtime.
39
+ #
40
+ # @return [Array<Processor>]
41
+ def sequential_processors: () -> untyped
42
+
43
+ private
44
+
45
+ # Normalize processor return values into ProcessorResult objects.
46
+ #
47
+ # @param result [Object] raw processor result
48
+ # @param event [ChangeEvent] processed event
49
+ # @return [ProcessorResult]
50
+ def normalize_result: (untyped result, untyped event) -> untyped
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,15 @@
1
+ module CDC
2
+ module Core
3
+ # Base error class for all cdc-core specific failures.
4
+ class Error < StandardError
5
+ end
6
+
7
+ # Raised when an operation cannot be normalized to a supported CDC action.
8
+ class InvalidOperationError < Error
9
+ end
10
+
11
+ # Raised by processors when a processor-specific failure needs wrapping.
12
+ class ProcessorError < Error
13
+ end
14
+ end
15
+ end