takagi 0.1.0 → 1.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.
Files changed (197) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +70 -7
  3. data/.yard/templates/default/layout/html/layout.erb +34 -0
  4. data/AGENTS.md +16 -0
  5. data/CHANGELOG.md +158 -1
  6. data/CODE_OF_CONDUCT.md +1 -1
  7. data/README.md +590 -23
  8. data/ROADMAP.md +55 -0
  9. data/Rakefile +4 -4
  10. data/Steepfile +39 -0
  11. data/bin/takagi-dev +159 -0
  12. data/docs/FIRST_PLUGIN_GUIDE.md +224 -0
  13. data/docs/HOOKS.md +31 -0
  14. data/examples/client_lifecycle_example.rb +118 -0
  15. data/examples/cloud_gateway_app.rb +217 -0
  16. data/examples/nested_api_app.rb +258 -0
  17. data/examples/simple_device_app.rb +71 -0
  18. data/examples/takagi.yml +138 -0
  19. data/lib/takagi/application.rb +256 -0
  20. data/lib/takagi/base/middleware_management.rb +39 -0
  21. data/lib/takagi/base/plugin_management.rb +75 -0
  22. data/lib/takagi/base/reactor_management.rb +104 -0
  23. data/lib/takagi/base/server_lifecycle.rb +156 -0
  24. data/lib/takagi/base.rb +103 -11
  25. data/lib/takagi/branding.rb +88 -0
  26. data/lib/takagi/cbor/decoder.rb +385 -0
  27. data/lib/takagi/cbor/encoder.rb +260 -0
  28. data/lib/takagi/cbor/error.rb +17 -0
  29. data/lib/takagi/cbor/version.rb +9 -0
  30. data/lib/takagi/client/response.rb +236 -0
  31. data/lib/takagi/client.rb +265 -0
  32. data/lib/takagi/client_base.rb +204 -0
  33. data/lib/takagi/coap/code_helpers.rb +190 -0
  34. data/lib/takagi/coap/registries/base.rb +165 -0
  35. data/lib/takagi/coap/registries/content_format.rb +71 -0
  36. data/lib/takagi/coap/registries/message_type.rb +69 -0
  37. data/lib/takagi/coap/registries/method.rb +38 -0
  38. data/lib/takagi/coap/registries/option.rb +71 -0
  39. data/lib/takagi/coap/registries/response.rb +93 -0
  40. data/lib/takagi/coap/registries/signaling.rb +34 -0
  41. data/lib/takagi/coap/signaling.rb +10 -0
  42. data/lib/takagi/coap.rb +37 -0
  43. data/lib/takagi/composite_router.rb +186 -0
  44. data/lib/takagi/config.rb +337 -0
  45. data/lib/takagi/controller/resource_allocator.rb +164 -0
  46. data/lib/takagi/controller/thread_pool.rb +144 -0
  47. data/lib/takagi/controller.rb +319 -0
  48. data/lib/takagi/core/attribute_set.rb +128 -0
  49. data/lib/takagi/discovery/core_link_format.rb +137 -0
  50. data/lib/takagi/errors.rb +536 -0
  51. data/lib/takagi/event_bus/address_prefix.rb +142 -0
  52. data/lib/takagi/event_bus/async_executor.rb +235 -0
  53. data/lib/takagi/event_bus/coap_bridge.rb +208 -0
  54. data/lib/takagi/event_bus/future.rb +153 -0
  55. data/lib/takagi/event_bus/lru_cache.rb +157 -0
  56. data/lib/takagi/event_bus/message_buffer.rb +237 -0
  57. data/lib/takagi/event_bus/observer_cleanup.rb +110 -0
  58. data/lib/takagi/event_bus/scope.rb +74 -0
  59. data/lib/takagi/event_bus.rb +594 -0
  60. data/lib/takagi/helpers.rb +88 -0
  61. data/lib/takagi/hooks.rb +82 -0
  62. data/lib/takagi/initializer.rb +18 -0
  63. data/lib/takagi/logger.rb +15 -6
  64. data/lib/takagi/message/base.rb +155 -0
  65. data/lib/takagi/message/deduplication_cache.rb +84 -0
  66. data/lib/takagi/message/inbound.rb +147 -0
  67. data/lib/takagi/message/outbound.rb +223 -0
  68. data/lib/takagi/message/request.rb +158 -0
  69. data/lib/takagi/message/retransmission_manager.rb +193 -0
  70. data/lib/takagi/middleware/authentication.rb +19 -0
  71. data/lib/takagi/middleware/caching.rb +23 -0
  72. data/lib/takagi/middleware/debugging.rb +16 -0
  73. data/lib/takagi/middleware/logging.rb +14 -0
  74. data/lib/takagi/middleware/metrics.rb +440 -0
  75. data/lib/takagi/middleware/rate_limiting.rb +24 -0
  76. data/lib/takagi/middleware_stack.rb +166 -0
  77. data/lib/takagi/network/base.rb +76 -0
  78. data/lib/takagi/network/framing/tcp.rb +222 -0
  79. data/lib/takagi/network/framing/udp.rb +110 -0
  80. data/lib/takagi/network/registry.rb +72 -0
  81. data/lib/takagi/network/tcp.rb +60 -0
  82. data/lib/takagi/network/tcp_sender.rb +21 -0
  83. data/lib/takagi/network/udp.rb +61 -0
  84. data/lib/takagi/network/udp_sender.rb +20 -0
  85. data/lib/takagi/observable/emitter.rb +62 -0
  86. data/lib/takagi/observable/reactor.rb +488 -0
  87. data/lib/takagi/observable/registry.rb +122 -0
  88. data/lib/takagi/observe_registry.rb +10 -0
  89. data/lib/takagi/observer/client.rb +68 -0
  90. data/lib/takagi/observer/registry.rb +137 -0
  91. data/lib/takagi/observer/sender.rb +39 -0
  92. data/lib/takagi/observer/watcher.rb +43 -0
  93. data/lib/takagi/plugin.rb +313 -0
  94. data/lib/takagi/profiles.rb +176 -0
  95. data/lib/takagi/reactor.rb +23 -0
  96. data/lib/takagi/reactor_registry.rb +64 -0
  97. data/lib/takagi/registry/base.rb +268 -0
  98. data/lib/takagi/response_builder.rb +141 -0
  99. data/lib/takagi/router/metadata_extractor.rb +133 -0
  100. data/lib/takagi/router/route_matcher.rb +83 -0
  101. data/lib/takagi/router.rb +284 -25
  102. data/lib/takagi/serialization/base.rb +102 -0
  103. data/lib/takagi/serialization/cbor_serializer.rb +92 -0
  104. data/lib/takagi/serialization/json_serializer.rb +96 -0
  105. data/lib/takagi/serialization/octet_stream_serializer.rb +82 -0
  106. data/lib/takagi/serialization/registry.rb +187 -0
  107. data/lib/takagi/serialization/text_serializer.rb +87 -0
  108. data/lib/takagi/serialization.rb +117 -0
  109. data/lib/takagi/server/multi.rb +41 -0
  110. data/lib/takagi/server/registry.rb +71 -0
  111. data/lib/takagi/server/tcp.rb +249 -0
  112. data/lib/takagi/server/udp.rb +139 -0
  113. data/lib/takagi/server/udp_worker.rb +174 -0
  114. data/lib/takagi/server.rb +1 -31
  115. data/lib/takagi/server_registry.rb +10 -0
  116. data/lib/takagi/tcp_client.rb +142 -0
  117. data/lib/takagi/version.rb +2 -1
  118. data/lib/takagi.rb +24 -3
  119. data/sig/takagi/application.rbs +48 -0
  120. data/sig/takagi/base/middleware_management.rbs +33 -0
  121. data/sig/takagi/base/reactor_management.rbs +52 -0
  122. data/sig/takagi/base/server_lifecycle.rbs +54 -0
  123. data/sig/takagi/base.rbs +48 -0
  124. data/sig/takagi/cbor/decoder.rbs +171 -0
  125. data/sig/takagi/cbor/encoder.rbs +146 -0
  126. data/sig/takagi/cbor/error.rbs +19 -0
  127. data/sig/takagi/cbor/version.rbs +7 -0
  128. data/sig/takagi/client/response.rbs +148 -0
  129. data/sig/takagi/client.rbs +119 -0
  130. data/sig/takagi/client_base.rbs +135 -0
  131. data/sig/takagi/coap/code_helpers.rbs +91 -0
  132. data/sig/takagi/coap/registries/base.rbs +95 -0
  133. data/sig/takagi/coap/registries/content_format.rbs +47 -0
  134. data/sig/takagi/coap/registries/message_type.rbs +53 -0
  135. data/sig/takagi/coap/registries/method.rbs +27 -0
  136. data/sig/takagi/coap/registries/option.rbs +43 -0
  137. data/sig/takagi/coap/registries/response.rbs +52 -0
  138. data/sig/takagi/coap.rbs +24 -0
  139. data/sig/takagi/composite_router.rbs +46 -0
  140. data/sig/takagi/config.rbs +134 -0
  141. data/sig/takagi/controller.rbs +73 -0
  142. data/sig/takagi/core/attribute_set.rbs +57 -0
  143. data/sig/takagi/discovery/core_link_format.rbs +50 -0
  144. data/sig/takagi/event_bus/address_prefix.rbs +78 -0
  145. data/sig/takagi/event_bus/async_executor.rbs +88 -0
  146. data/sig/takagi/event_bus/coap_bridge.rbs +93 -0
  147. data/sig/takagi/event_bus/future.rbs +78 -0
  148. data/sig/takagi/event_bus/lru_cache.rbs +86 -0
  149. data/sig/takagi/event_bus/message_buffer.rbs +133 -0
  150. data/sig/takagi/event_bus/observer_cleanup.rbs +62 -0
  151. data/sig/takagi/event_bus.rbs +320 -0
  152. data/sig/takagi/helpers.rbs +34 -0
  153. data/sig/takagi/initializer.rbs +9 -0
  154. data/sig/takagi/logger.rbs +17 -0
  155. data/sig/takagi/message/base.rbs +64 -0
  156. data/sig/takagi/message/deduplication_cache.rbs +49 -0
  157. data/sig/takagi/message/inbound.rbs +76 -0
  158. data/sig/takagi/message/outbound.rbs +48 -0
  159. data/sig/takagi/message/request.rbs +32 -0
  160. data/sig/takagi/message/retransmission_manager.rbs +76 -0
  161. data/sig/takagi/middleware/authentication.rbs +11 -0
  162. data/sig/takagi/middleware/caching.rbs +13 -0
  163. data/sig/takagi/middleware/debugging.rbs +9 -0
  164. data/sig/takagi/middleware/logging.rbs +7 -0
  165. data/sig/takagi/middleware/metrics.rbs +15 -0
  166. data/sig/takagi/middleware/rate_limiting.rbs +13 -0
  167. data/sig/takagi/middleware_stack.rbs +69 -0
  168. data/sig/takagi/network/tcp_sender.rbs +10 -0
  169. data/sig/takagi/network/udp_sender.rbs +14 -0
  170. data/sig/takagi/observe_registry.rbs +36 -0
  171. data/sig/takagi/observer/client.rbs +36 -0
  172. data/sig/takagi/observer/sender.rbs +12 -0
  173. data/sig/takagi/observer/watcher.rbs +18 -0
  174. data/sig/takagi/profiles.rbs +33 -0
  175. data/sig/takagi/reactor.rbs +20 -0
  176. data/sig/takagi/reactor_registry.rbs +14 -0
  177. data/sig/takagi/response_builder.rbs +12 -0
  178. data/sig/takagi/router/metadata_extractor.rbs +71 -0
  179. data/sig/takagi/router/route_matcher.rbs +43 -0
  180. data/sig/takagi/router.rbs +166 -0
  181. data/sig/takagi/serialization.rbs +32 -0
  182. data/sig/takagi/server/multi.rbs +16 -0
  183. data/sig/takagi/server/tcp.rbs +42 -0
  184. data/sig/takagi/server/udp.rbs +52 -0
  185. data/sig/takagi/server/udp_worker.rbs +42 -0
  186. data/sig/takagi/server.rbs +4 -0
  187. data/sig/takagi/server_registry.rbs +71 -0
  188. data/sig/takagi/tcp_client.rbs +23 -0
  189. data/sig/takagi/version.rbs +5 -0
  190. data/takagi.gemspec +37 -35
  191. metadata +204 -31
  192. data/.idea/.gitignore +0 -8
  193. data/.idea/misc.xml +0 -4
  194. data/.idea/modules.xml +0 -8
  195. data/.idea/takagi.iml +0 -81
  196. data/.idea/vcs.xml +0 -6
  197. data/lib/takagi/message.rb +0 -75
@@ -0,0 +1,320 @@
1
+ module Takagi
2
+ # High-level event distribution with threaded/process async delivery.
3
+ # Built on top of ObserveRegistry with zero runtime dependencies.
4
+ #
5
+ # Supports both CoAP Observe style and Pub/Sub style APIs:
6
+ #
7
+ # @example CoAP Observe style
8
+ # EventBus.observe('sensor.temperature.room1') { |msg| puts msg.body }
9
+ # EventBus.notify('sensor.temperature.room1', { value: 25.5 })
10
+ #
11
+ # @example Pub/Sub style (aliases)
12
+ # EventBus.consumer('sensor.temperature.room1') { |msg| puts msg.body }
13
+ # EventBus.publish('sensor.temperature.room1', { value: 25.5 })
14
+ #
15
+ # @example Request-Reply pattern
16
+ # reply = EventBus.send_sync('cache.query', { key: 'user:123' }, timeout: 1.0)
17
+ class EventBus
18
+ self.@executor: untyped
19
+
20
+ self.@handlers: untyped
21
+
22
+ self.@consumers: untyped
23
+
24
+ self.@handler_store: untyped
25
+
26
+ self.@mutex: untyped
27
+
28
+ self.@current_states: untyped
29
+
30
+ self.@cleanup: untyped
31
+
32
+ self.@last_index: untyped
33
+
34
+ self.@message_store: untyped
35
+
36
+ class Error < StandardError
37
+ end
38
+
39
+ # Event message wrapper (shareable for Ractor)
40
+ class Message
41
+ @address: untyped
42
+
43
+ @body: untyped
44
+
45
+ @headers: untyped
46
+
47
+ @reply_address: untyped
48
+
49
+ @timestamp: untyped
50
+
51
+ attr_reader address: untyped
52
+
53
+ attr_reader body: untyped
54
+
55
+ attr_reader headers: untyped
56
+
57
+ attr_reader reply_address: untyped
58
+
59
+ attr_reader timestamp: untyped
60
+
61
+ def initialize: (untyped address, untyped body, ?headers: ::Hash[untyped, untyped], ?reply_address: untyped?) -> void
62
+
63
+ # Reply to this message (request-reply pattern)
64
+ def reply: (untyped body, ?headers: ::Hash[untyped, untyped]) -> (nil | untyped)
65
+
66
+ private
67
+
68
+ def deep_freeze: (untyped obj) -> untyped
69
+ end
70
+
71
+ # Event handler wrapper
72
+ class Handler
73
+ @address: untyped
74
+
75
+ @block: untyped
76
+
77
+ @options: untyped
78
+
79
+ @local_only: untyped
80
+
81
+ @pool_id: untyped
82
+
83
+ attr_reader address: untyped
84
+
85
+ attr_reader block: untyped
86
+
87
+ attr_reader options: untyped
88
+
89
+ attr_reader pool_id: untyped
90
+
91
+ def initialize: (untyped address, ?::Hash[untyped, untyped] options) { (?) -> untyped } -> void
92
+
93
+ def call: (untyped message) -> untyped
94
+
95
+ def local_only?: () -> untyped
96
+ end
97
+
98
+ # Helper method to get configuration with fallback to ENV
99
+ def self.config_value: (untyped config_key, untyped env_key, untyped default) -> untyped
100
+
101
+ # Publish message to all subscribers (pub/sub pattern)
102
+ # @param address [String] Event address (e.g., "sensor.temperature.room1")
103
+ # @param body [Object] Message body (must be shareable for Ractors)
104
+ # @param headers [Hash] Optional message headers
105
+ # @return [Message] Published message
106
+ #
107
+ # @example
108
+ # EventBus.publish('sensor.temperature.room1', { value: 25.5, unit: 'C' })
109
+ def self.publish: (untyped address, ?untyped? body, ?headers: ::Hash[untyped, untyped]) -> untyped
110
+
111
+ # Send message to single consumer (point-to-point pattern)
112
+ # Uses round-robin if multiple consumers registered
113
+ # @param address [String] Event address
114
+ # @param body [Object] Message body
115
+ # @param headers [Hash] Optional headers
116
+ # @yield [reply_message] Optional reply handler (request-reply pattern)
117
+ # @return [Message] Sent message
118
+ #
119
+ # @example
120
+ # EventBus.send('cache.query', { key: 'user:123' }) do |reply|
121
+ # puts "Cache value: #{reply.body[:value]}"
122
+ # end
123
+ def self.send: (untyped address, ?untyped? body, ?headers: ::Hash[untyped, untyped]) ?{ (?) -> untyped } -> untyped
124
+
125
+ # Synchronous send with timeout (blocking)
126
+ # @param address [String] Event address
127
+ # @param body [Object] Message body
128
+ # @param headers [Hash] Optional headers
129
+ # @param timeout [Float] Timeout in seconds (default: 1.0)
130
+ # @return [Message] Reply message
131
+ # @raise [Timeout::Error] If no reply received within timeout
132
+ #
133
+ # @example
134
+ # reply = EventBus.send_sync('cache.query', { key: 'user:123' }, timeout: 1.0)
135
+ # puts "Cache hit: #{reply.body[:hit]}"
136
+ def self.send_sync: (untyped address, ?untyped? body, ?headers: ::Hash[untyped, untyped], ?timeout: ::Float) -> untyped
137
+
138
+ # Asynchronous send with Future (non-blocking)
139
+ # @param address [String] Event address
140
+ # @param body [Object] Message body
141
+ # @param headers [Hash] Optional headers
142
+ # @return [Future] Future that will contain reply
143
+ #
144
+ # @example
145
+ # future = EventBus.send_async('cache.query', { key: 'user:123' })
146
+ # # ... do other work ...
147
+ # reply = future.value(timeout: 1.0)
148
+ def self.send_async: (untyped address, ?untyped? body, ?headers: ::Hash[untyped, untyped]) -> untyped
149
+
150
+ # Register consumer for address (point-to-point or pub/sub)
151
+ # @param address [String] Event address or pattern
152
+ # @param options [Hash] Handler options
153
+ # @option options [Boolean] :local_only Only receive local messages
154
+ # @yield [message] Block called when message received
155
+ # @return [String] Consumer ID (for unregistering)
156
+ #
157
+ # @example
158
+ # id = EventBus.consumer('sensor.temperature.room1') do |message|
159
+ # puts "Temp: #{message.body[:value]}"
160
+ # end
161
+ # EventBus.unregister(id)
162
+ def self.consumer: (untyped address, ?::Hash[untyped, untyped] options) ?{ (?) -> untyped } -> untyped
163
+
164
+ # Unregister a consumer
165
+ # @param consumer_id [String] Consumer ID returned from consumer()
166
+ def self.unregister: (untyped consumer_id) -> (nil | untyped)
167
+
168
+ # Subscribe to remote CoAP observable
169
+ # @param address [String] Event address
170
+ # @param node_url [String] Remote node URL (e.g., 'coap://building-a:5683')
171
+ # @yield [message] Block called when remote notification received
172
+ # @return [String] Subscription ID
173
+ #
174
+ # @example
175
+ # id = EventBus.subscribe_remote('sensor.temp.buildingA', 'coap://building-a:5683') do |msg|
176
+ # puts "Remote temp: #{msg.body[:value]}"
177
+ # end
178
+ def self.subscribe_remote: (untyped address, untyped node_url) { (?) -> untyped } -> untyped
179
+
180
+ alias self.observe self.consumer
181
+
182
+ alias self.on self.consumer
183
+
184
+ alias self.notify self.publish
185
+
186
+ alias self.emit self.publish
187
+
188
+ alias self.cancel self.unregister
189
+
190
+ alias self.subscribe self.subscribe_remote
191
+
192
+ alias self.unsubscribe self.unregister
193
+
194
+ # Configure message store (for buffering/replay)
195
+ # @param store [MessageBuffer, Object] Message store instance (nil to disable)
196
+ # @return [MessageBuffer, Object, nil] Configured store
197
+ #
198
+ # @example Enable default buffering
199
+ # EventBus.enable_message_buffering
200
+ #
201
+ # @example Custom configuration
202
+ # EventBus.configure_message_store(
203
+ # MessageBuffer.new(max_messages: 200, ttl: 600)
204
+ # )
205
+ #
206
+ # @example Custom plugin store
207
+ # EventBus.configure_message_store(RedisMessageStore.new)
208
+ def self.configure_message_store: (untyped store) -> untyped
209
+
210
+ # Enable default message buffering
211
+ # @param max_messages [Integer] Max messages per address
212
+ # @param ttl [Integer] Time-to-live in seconds
213
+ # @return [MessageBuffer] Configured buffer
214
+ def self.enable_message_buffering: (?max_messages: ::Integer, ?ttl: ::Integer) -> untyped
215
+
216
+ # Disable message buffering
217
+ def self.disable_message_buffering: () -> untyped
218
+
219
+ # Get current message store
220
+ # @return [MessageBuffer, Object, nil] Current message store
221
+ attr_reader self.message_store: untyped
222
+
223
+ # Replay buffered messages for an address
224
+ # @param address [String] Event address
225
+ # @param since [Time, nil] Return messages since this time (nil = all)
226
+ # @return [Array<Message>] Buffered messages
227
+ #
228
+ # @example Replay all buffered messages
229
+ # EventBus.replay('sensor.temperature.room1')
230
+ #
231
+ # @example Replay last 60 seconds
232
+ # EventBus.replay('sensor.temperature.room1', since: Time.now - 60)
233
+ def self.replay: (untyped address, ?since: untyped?) -> (::Array[untyped] | untyped)
234
+
235
+ # Replay buffered messages to a consumer
236
+ # Useful for late joiners or reconnecting nodes
237
+ # @param address [String] Event address
238
+ # @param since [Time, nil] Replay messages since this time
239
+ # @yield [message] Block called for each buffered message
240
+ #
241
+ # @example Replay to new subscriber
242
+ # EventBus.consumer('sensor.temperature.room1') do |msg|
243
+ # puts "Temp: #{msg.body[:value]}"
244
+ # end
245
+ # # Catch up on last 5 minutes
246
+ # EventBus.replay_to('sensor.temperature.room1', since: Time.now - 300) do |msg|
247
+ # puts "Missed: #{msg.body[:value]}"
248
+ # end
249
+ def self.replay_to: (untyped address, ?since: untyped?) ?{ (?) -> untyped } -> untyped
250
+
251
+ # Check if address is distributed via CoAP
252
+ # Uses AddressPrefix registry for extensibility
253
+ # @param address [String] Event address
254
+ # @return [Boolean]
255
+ def self.distributed?: (untyped address) -> untyped
256
+
257
+ # Check if address is local-only
258
+ # Uses AddressPrefix registry for extensibility
259
+ # @param address [String] Event address
260
+ # @return [Boolean]
261
+ def self.local_only?: (untyped address) -> untyped
262
+
263
+ # Get current state for address
264
+ # @param address [String] Event address
265
+ # @return [Object, nil] Current state or nil
266
+ def self.current_state: (untyped address) -> untyped
267
+
268
+ # List all registered addresses
269
+ # @return [Array<String>] Event addresses
270
+ def self.addresses: () -> untyped
271
+
272
+ # Get handler count for address
273
+ # @param address [String] Event address
274
+ # @return [Integer] Number of handlers
275
+ def self.handler_count: (untyped address) -> untyped
276
+
277
+ # Get EventBus statistics
278
+ # @return [Hash] Statistics
279
+ def self.stats: () -> untyped
280
+
281
+ # Start background cleanup
282
+ def self.start_cleanup: () -> untyped
283
+
284
+ # Stop background cleanup
285
+ def self.stop_cleanup: () -> untyped
286
+
287
+ # Shutdown EventBus (cleanup resources)
288
+ def self.shutdown: () -> untyped
289
+
290
+ private
291
+
292
+ # Get all handlers for exact address match
293
+ def self.handlers_for: (untyped address) -> untyped
294
+
295
+ # Get handlers matching wildcard patterns
296
+ # Supports: "sensor.*", "sensor.*.room1"
297
+ def self.wildcard_handlers: (untyped address) -> untyped
298
+
299
+ # Match address parts against pattern
300
+ def self.match_pattern?: (untyped parts, untyped pattern_parts) -> (false | untyped)
301
+
302
+ # Round-robin selection of next handler
303
+ def self.next_handler_for: (untyped address) -> (nil | untyped)
304
+
305
+ public
306
+
307
+ def self.handler_for_pool_id: (untyped pool_id) -> untyped
308
+
309
+ private
310
+
311
+ # Deliver message asynchronously via Ractor pool
312
+ def self.deliver_async: (untyped handler, untyped message) -> untyped
313
+
314
+ # Generate unique reply address
315
+ def self.generate_reply_address: () -> ::String
316
+
317
+ # Log debug message
318
+ def self.log_debug: (untyped message) -> (nil | untyped)
319
+ end
320
+ end
@@ -0,0 +1,34 @@
1
+ module Takagi
2
+ # Helper methods for route handlers to improve DX
3
+ #
4
+ # Dynamically generates response helper methods from CoAP::Registries::Response registry:
5
+ # - Success methods (2.xx): created(data = {}), changed(data = {}), etc.
6
+ # - Error methods (4.xx, 5.xx): bad_request(message = ...), not_found(message = ...), etc.
7
+ #
8
+ # @example Success response
9
+ # created({ id: 123, name: 'Resource' })
10
+ #
11
+ # @example Error response
12
+ # bad_request('Invalid input')
13
+ # unauthorized({ error: 'Token expired' })
14
+ module Helpers
15
+ # Returns a JSON response with 2.05 Content status and sets Content-Format to application/json
16
+ # Similar to Sinatra's json helper, automatically sets the Content-Format option
17
+ # @param data [Hash] The data to return as JSON
18
+ # @return [Takagi::Message::Outbound] The response with JSON content-format
19
+ def json: (?::Hash[untyped, untyped] data) -> Message::Outbound
20
+
21
+ # Validates that required parameters are present
22
+ # @param required_params [Array<Symbol>] List of required parameter names
23
+ # @raise [StandardError] If any required parameter is missing
24
+ def validate_params: (*untyped required_params) -> (nil | untyped)
25
+
26
+ # Halts execution and returns the given response
27
+ # Useful for early returns
28
+ # @param response [Takagi::Message::Outbound, Hash] The response to return
29
+ def halt: (untyped response) -> untyped
30
+
31
+ # Alias for internal_server_error (more concise)
32
+ alias server_error internal_server_error
33
+ end
34
+ end
@@ -0,0 +1,9 @@
1
+ module Takagi
2
+ # Let's do some initialization
3
+ class Initializer
4
+ # Runs the initialization logic (e.g., loading configurations, setting up databases)
5
+ def self.run!: () -> untyped
6
+
7
+ def self.load_initializers: () -> untyped
8
+ end
9
+ end
@@ -0,0 +1,17 @@
1
+ module Takagi
2
+ class Logger
3
+ @logger: untyped
4
+
5
+ def initialize: (?log_output: untyped, ?level: untyped) -> void
6
+
7
+ def set_level: (untyped level) -> untyped
8
+
9
+ def info: (untyped message) -> untyped
10
+
11
+ def warn: (untyped message) -> untyped
12
+
13
+ def debug: (untyped message) -> untyped
14
+
15
+ def error: (untyped message) -> untyped
16
+ end
17
+ end
@@ -0,0 +1,64 @@
1
+ module Takagi
2
+ module Message
3
+ # Base class for message
4
+ class Base
5
+ @data: untyped
6
+
7
+ @logger: untyped
8
+
9
+ @version: untyped
10
+
11
+ @type: untyped
12
+
13
+ @code: untyped
14
+
15
+ @message_id: untyped
16
+
17
+ @token: untyped
18
+
19
+ @options: untyped
20
+
21
+ @payload: untyped
22
+
23
+ attr_reader version: untyped
24
+
25
+ attr_reader type: untyped
26
+
27
+ attr_reader token: untyped
28
+
29
+ attr_reader message_id: untyped
30
+
31
+ attr_reader payload: untyped
32
+
33
+ attr_reader options: untyped
34
+
35
+ attr_reader code: untyped
36
+
37
+ def initialize: (?untyped? data) -> void
38
+
39
+ # Convert CoAP code number to method name using registry
40
+ # @param code [Integer] CoAP code number
41
+ # @return [String] Method name (e.g., 'GET', 'OBSERVE')
42
+ def coap_code_to_method: (untyped code) -> ("OBSERVE" | untyped)
43
+
44
+ # Convert method name to CoAP code number using registry
45
+ # @param method [String, Symbol] Method name (e.g., 'GET', :post)
46
+ # @return [Integer] CoAP code number
47
+ def coap_method_to_code: (untyped method) -> untyped
48
+
49
+ private
50
+
51
+ def parse: (untyped data) -> untyped
52
+
53
+ def parse_options: (untyped bytes) -> untyped
54
+
55
+ def extract_payload: (untyped data) -> (nil | untyped)
56
+
57
+ def decode_extended_value: (untyped bytes, untyped position, untyped raw_value) -> untyped
58
+
59
+ def store_option: (untyped options, untyped option_number, untyped value) -> untyped
60
+
61
+ def coerce_option_value: (untyped value) -> untyped
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,49 @@
1
+ module Takagi
2
+ module Message
3
+ # Implements message deduplication as per RFC 7252 Section 4.4
4
+ #
5
+ # The server MUST detect duplicates by matching both Message ID and source endpoint.
6
+ # When a duplicate CON message is received, the server MUST resend the cached response.
7
+ #
8
+ # Cache entries expire after EXCHANGE_LIFETIME (247 seconds per RFC 7252 §4.8.2)
9
+ class DeduplicationCache
10
+ @cache: untyped
11
+
12
+ @mutex: untyped
13
+
14
+ # RFC 7252 §4.8.2: EXCHANGE_LIFETIME = 247 seconds
15
+ EXCHANGE_LIFETIME: 247
16
+
17
+ # Entry contains the cached response and metadata
18
+ CacheEntry: untyped
19
+
20
+ def initialize: () -> void
21
+
22
+ # Check if message is a duplicate and return cached response if available
23
+ # @param message_id [Integer] The CoAP Message ID
24
+ # @param source_endpoint [String] Source IP:Port identifier
25
+ # @return [String, nil] Cached response data or nil if not a duplicate
26
+ def check_duplicate: (untyped message_id, untyped source_endpoint) -> untyped
27
+
28
+ # Store a response for future duplicate detection
29
+ # @param message_id [Integer] The CoAP Message ID
30
+ # @param source_endpoint [String] Source IP:Port identifier
31
+ # @param response_data [String] The serialized response to cache
32
+ def store_response: (untyped message_id, untyped source_endpoint, untyped response_data) -> untyped
33
+
34
+ # Clear all cache entries (useful for testing)
35
+ def clear: () -> untyped
36
+
37
+ # Get cache statistics
38
+ def stats: () -> untyped
39
+
40
+ private
41
+
42
+ def cache_key: (untyped message_id, untyped source_endpoint) -> ::String
43
+
44
+ # Remove expired entries to prevent unbounded memory growth
45
+ # RFC 7252 §4.8.2: entries older than EXCHANGE_LIFETIME should be discarded
46
+ def cleanup_expired_entries: () -> untyped
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,76 @@
1
+ module Takagi
2
+ module Message
3
+ # Class for inbound message that is coming to server
4
+ class Inbound < Base
5
+ @method: untyped
6
+
7
+ @response_code: untyped
8
+
9
+ @uri: untyped
10
+
11
+ attr_reader method: untyped
12
+
13
+ attr_reader uri: untyped
14
+
15
+ attr_reader response_code: untyped
16
+
17
+ def initialize: (untyped data) -> void
18
+
19
+ def to_response: (untyped code, untyped payload, ?options: ::Hash[untyped, untyped]) -> untyped
20
+
21
+ def parse_coap_uri: () -> untyped
22
+
23
+ # Get CoAP option by number
24
+ # @param option_number [Integer] The CoAP option number
25
+ # @return [Object, nil] The option value
26
+ def option: (untyped option_number) -> untyped
27
+
28
+ # Check if request has a specific CoAP option
29
+ # @param option_number [Integer] The CoAP option number
30
+ # @return [Boolean]
31
+ def option?: (untyped option_number) -> untyped
32
+
33
+ # Get Accept option
34
+ # @return [Integer, nil] The Accept content format
35
+ def accept: () -> untyped
36
+
37
+ # Check if request accepts a specific content format
38
+ # @param format [String, Integer] Format name or number
39
+ # @return [Boolean]
40
+ def accept?: (untyped format) -> (false | untyped)
41
+
42
+ # Get Content-Format option
43
+ # @return [Integer, nil] The Content-Format
44
+ def content_format: () -> untyped
45
+
46
+ # Get query parameters as a hash
47
+ # @return [Hash<String, String>]
48
+ def query_params: () -> (::Hash[untyped, untyped] | untyped)
49
+
50
+ # Check if request is a GET
51
+ # @return [Boolean]
52
+ def get?: () -> untyped
53
+
54
+ # Check if request is a POST
55
+ # @return [Boolean]
56
+ def post?: () -> untyped
57
+
58
+ # Check if request is a PUT
59
+ # @return [Boolean]
60
+ def put?: () -> untyped
61
+
62
+ # Check if request is a DELETE
63
+ # @return [Boolean]
64
+ def delete?: () -> untyped
65
+
66
+ # Check if request is an OBSERVE
67
+ # @return [Boolean]
68
+ def observe?: () -> untyped
69
+
70
+ private
71
+
72
+ # Convert content format name to number using CoAP registry
73
+ def content_format_to_number: (untyped format) -> untyped
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,48 @@
1
+ module Takagi
2
+ module Message
3
+ # Class for outbound message that is coming from server
4
+ class Outbound < Base
5
+ @code: untyped
6
+
7
+ @token: untyped
8
+
9
+ @message_id: untyped
10
+
11
+ @type: untyped
12
+
13
+ @options: untyped
14
+
15
+ @payload: untyped
16
+
17
+ def initialize: (code: untyped, payload: untyped, ?token: untyped?, ?message_id: untyped?, ?type: untyped, ?options: ::Hash[untyped, untyped]) -> void
18
+
19
+ def to_bytes: () -> untyped
20
+
21
+ private
22
+
23
+ def with_error_handling: () { () -> untyped } -> untyped
24
+
25
+ def log_generation: () -> untyped
26
+
27
+ def build_header: () -> untyped
28
+
29
+ def token_bytes: () -> untyped
30
+
31
+ def build_options_section: () -> untyped
32
+
33
+ def build_payload_section: () -> untyped
34
+
35
+ def log_final_packet: (untyped packet) -> untyped
36
+
37
+ def normalize_options: (untyped options) -> untyped
38
+
39
+ def flattened_options: () -> untyped
40
+
41
+ def encode_option_value: (untyped value) -> untyped
42
+
43
+ def encode_integer_option_value: (untyped value) -> untyped
44
+
45
+ def encode_option_header_value: (untyped value) -> untyped
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,32 @@
1
+ module Takagi
2
+ module Message
3
+ # Encodes outbound CoAP request envelopes used when observing remote peers.
4
+ class Request < Base
5
+ @method: untyped
6
+
7
+ @uri: untyped
8
+
9
+ @token: untyped
10
+
11
+ @observe: untyped
12
+
13
+ @message_id: untyped
14
+
15
+ @payload: untyped
16
+
17
+ attr_reader message_id: untyped
18
+
19
+ def initialize: (method: untyped, uri: untyped, ?payload: untyped?, ?token: untyped?, **untyped options) -> void
20
+
21
+ def to_bytes: () -> untyped
22
+
23
+ private
24
+
25
+ def encode_options: () -> untyped
26
+
27
+ def encode_option: (untyped option_number, untyped value, untyped last_option_number) -> untyped
28
+
29
+ def encode_extended: (untyped val) -> untyped
30
+ end
31
+ end
32
+ end