spikard 0.4.0-x86_64-linux

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 (138) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +1 -0
  3. data/README.md +659 -0
  4. data/ext/spikard_rb/Cargo.toml +17 -0
  5. data/ext/spikard_rb/extconf.rb +10 -0
  6. data/ext/spikard_rb/src/lib.rs +6 -0
  7. data/lib/spikard/app.rb +405 -0
  8. data/lib/spikard/background.rb +27 -0
  9. data/lib/spikard/config.rb +396 -0
  10. data/lib/spikard/converters.rb +13 -0
  11. data/lib/spikard/handler_wrapper.rb +113 -0
  12. data/lib/spikard/provide.rb +214 -0
  13. data/lib/spikard/response.rb +173 -0
  14. data/lib/spikard/schema.rb +243 -0
  15. data/lib/spikard/sse.rb +111 -0
  16. data/lib/spikard/streaming_response.rb +44 -0
  17. data/lib/spikard/testing.rb +221 -0
  18. data/lib/spikard/upload_file.rb +131 -0
  19. data/lib/spikard/version.rb +5 -0
  20. data/lib/spikard/websocket.rb +59 -0
  21. data/lib/spikard.rb +43 -0
  22. data/sig/spikard.rbs +366 -0
  23. data/vendor/bundle/ruby/3.4.0/gems/diff-lcs-1.6.2/mise.toml +5 -0
  24. data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-dock-1.10.0/build/buildkitd.toml +2 -0
  25. data/vendor/crates/spikard-bindings-shared/Cargo.toml +63 -0
  26. data/vendor/crates/spikard-bindings-shared/examples/config_extraction.rs +139 -0
  27. data/vendor/crates/spikard-bindings-shared/src/config_extractor.rs +561 -0
  28. data/vendor/crates/spikard-bindings-shared/src/conversion_traits.rs +194 -0
  29. data/vendor/crates/spikard-bindings-shared/src/di_traits.rs +246 -0
  30. data/vendor/crates/spikard-bindings-shared/src/error_response.rs +403 -0
  31. data/vendor/crates/spikard-bindings-shared/src/handler_base.rs +274 -0
  32. data/vendor/crates/spikard-bindings-shared/src/lib.rs +25 -0
  33. data/vendor/crates/spikard-bindings-shared/src/lifecycle_base.rs +298 -0
  34. data/vendor/crates/spikard-bindings-shared/src/lifecycle_executor.rs +637 -0
  35. data/vendor/crates/spikard-bindings-shared/src/response_builder.rs +309 -0
  36. data/vendor/crates/spikard-bindings-shared/src/test_client_base.rs +248 -0
  37. data/vendor/crates/spikard-bindings-shared/src/validation_helpers.rs +355 -0
  38. data/vendor/crates/spikard-bindings-shared/tests/comprehensive_coverage.rs +502 -0
  39. data/vendor/crates/spikard-bindings-shared/tests/error_response_edge_cases.rs +389 -0
  40. data/vendor/crates/spikard-bindings-shared/tests/handler_base_integration.rs +413 -0
  41. data/vendor/crates/spikard-core/Cargo.toml +40 -0
  42. data/vendor/crates/spikard-core/src/bindings/mod.rs +3 -0
  43. data/vendor/crates/spikard-core/src/bindings/response.rs +133 -0
  44. data/vendor/crates/spikard-core/src/debug.rs +63 -0
  45. data/vendor/crates/spikard-core/src/di/container.rs +726 -0
  46. data/vendor/crates/spikard-core/src/di/dependency.rs +273 -0
  47. data/vendor/crates/spikard-core/src/di/error.rs +118 -0
  48. data/vendor/crates/spikard-core/src/di/factory.rs +538 -0
  49. data/vendor/crates/spikard-core/src/di/graph.rs +545 -0
  50. data/vendor/crates/spikard-core/src/di/mod.rs +192 -0
  51. data/vendor/crates/spikard-core/src/di/resolved.rs +411 -0
  52. data/vendor/crates/spikard-core/src/di/value.rs +283 -0
  53. data/vendor/crates/spikard-core/src/errors.rs +39 -0
  54. data/vendor/crates/spikard-core/src/http.rs +153 -0
  55. data/vendor/crates/spikard-core/src/lib.rs +29 -0
  56. data/vendor/crates/spikard-core/src/lifecycle.rs +422 -0
  57. data/vendor/crates/spikard-core/src/metadata.rs +397 -0
  58. data/vendor/crates/spikard-core/src/parameters.rs +723 -0
  59. data/vendor/crates/spikard-core/src/problem.rs +310 -0
  60. data/vendor/crates/spikard-core/src/request_data.rs +189 -0
  61. data/vendor/crates/spikard-core/src/router.rs +249 -0
  62. data/vendor/crates/spikard-core/src/schema_registry.rs +183 -0
  63. data/vendor/crates/spikard-core/src/type_hints.rs +304 -0
  64. data/vendor/crates/spikard-core/src/validation/error_mapper.rs +689 -0
  65. data/vendor/crates/spikard-core/src/validation/mod.rs +459 -0
  66. data/vendor/crates/spikard-http/Cargo.toml +58 -0
  67. data/vendor/crates/spikard-http/examples/sse-notifications.rs +147 -0
  68. data/vendor/crates/spikard-http/examples/websocket-chat.rs +91 -0
  69. data/vendor/crates/spikard-http/src/auth.rs +247 -0
  70. data/vendor/crates/spikard-http/src/background.rs +1562 -0
  71. data/vendor/crates/spikard-http/src/bindings/mod.rs +3 -0
  72. data/vendor/crates/spikard-http/src/bindings/response.rs +1 -0
  73. data/vendor/crates/spikard-http/src/body_metadata.rs +8 -0
  74. data/vendor/crates/spikard-http/src/cors.rs +490 -0
  75. data/vendor/crates/spikard-http/src/debug.rs +63 -0
  76. data/vendor/crates/spikard-http/src/di_handler.rs +1878 -0
  77. data/vendor/crates/spikard-http/src/handler_response.rs +532 -0
  78. data/vendor/crates/spikard-http/src/handler_trait.rs +861 -0
  79. data/vendor/crates/spikard-http/src/handler_trait_tests.rs +284 -0
  80. data/vendor/crates/spikard-http/src/lib.rs +524 -0
  81. data/vendor/crates/spikard-http/src/lifecycle/adapter.rs +149 -0
  82. data/vendor/crates/spikard-http/src/lifecycle.rs +428 -0
  83. data/vendor/crates/spikard-http/src/middleware/mod.rs +285 -0
  84. data/vendor/crates/spikard-http/src/middleware/multipart.rs +930 -0
  85. data/vendor/crates/spikard-http/src/middleware/urlencoded.rs +541 -0
  86. data/vendor/crates/spikard-http/src/middleware/validation.rs +287 -0
  87. data/vendor/crates/spikard-http/src/openapi/mod.rs +309 -0
  88. data/vendor/crates/spikard-http/src/openapi/parameter_extraction.rs +535 -0
  89. data/vendor/crates/spikard-http/src/openapi/schema_conversion.rs +867 -0
  90. data/vendor/crates/spikard-http/src/openapi/spec_generation.rs +678 -0
  91. data/vendor/crates/spikard-http/src/query_parser.rs +369 -0
  92. data/vendor/crates/spikard-http/src/response.rs +399 -0
  93. data/vendor/crates/spikard-http/src/server/handler.rs +1557 -0
  94. data/vendor/crates/spikard-http/src/server/lifecycle_execution.rs +98 -0
  95. data/vendor/crates/spikard-http/src/server/mod.rs +806 -0
  96. data/vendor/crates/spikard-http/src/server/request_extraction.rs +630 -0
  97. data/vendor/crates/spikard-http/src/server/routing_factory.rs +497 -0
  98. data/vendor/crates/spikard-http/src/sse.rs +961 -0
  99. data/vendor/crates/spikard-http/src/testing/form.rs +14 -0
  100. data/vendor/crates/spikard-http/src/testing/multipart.rs +60 -0
  101. data/vendor/crates/spikard-http/src/testing/test_client.rs +285 -0
  102. data/vendor/crates/spikard-http/src/testing.rs +377 -0
  103. data/vendor/crates/spikard-http/src/websocket.rs +831 -0
  104. data/vendor/crates/spikard-http/tests/background_behavior.rs +918 -0
  105. data/vendor/crates/spikard-http/tests/common/handlers.rs +308 -0
  106. data/vendor/crates/spikard-http/tests/common/mod.rs +21 -0
  107. data/vendor/crates/spikard-http/tests/di_integration.rs +202 -0
  108. data/vendor/crates/spikard-http/tests/doc_snippets.rs +4 -0
  109. data/vendor/crates/spikard-http/tests/lifecycle_execution.rs +1135 -0
  110. data/vendor/crates/spikard-http/tests/multipart_behavior.rs +688 -0
  111. data/vendor/crates/spikard-http/tests/server_config_builder.rs +324 -0
  112. data/vendor/crates/spikard-http/tests/sse_behavior.rs +728 -0
  113. data/vendor/crates/spikard-http/tests/websocket_behavior.rs +724 -0
  114. data/vendor/crates/spikard-rb/Cargo.toml +43 -0
  115. data/vendor/crates/spikard-rb/build.rs +199 -0
  116. data/vendor/crates/spikard-rb/src/background.rs +63 -0
  117. data/vendor/crates/spikard-rb/src/config/mod.rs +5 -0
  118. data/vendor/crates/spikard-rb/src/config/server_config.rs +283 -0
  119. data/vendor/crates/spikard-rb/src/conversion.rs +459 -0
  120. data/vendor/crates/spikard-rb/src/di/builder.rs +105 -0
  121. data/vendor/crates/spikard-rb/src/di/mod.rs +413 -0
  122. data/vendor/crates/spikard-rb/src/handler.rs +612 -0
  123. data/vendor/crates/spikard-rb/src/integration/mod.rs +3 -0
  124. data/vendor/crates/spikard-rb/src/lib.rs +1857 -0
  125. data/vendor/crates/spikard-rb/src/lifecycle.rs +275 -0
  126. data/vendor/crates/spikard-rb/src/metadata/mod.rs +5 -0
  127. data/vendor/crates/spikard-rb/src/metadata/route_extraction.rs +427 -0
  128. data/vendor/crates/spikard-rb/src/runtime/mod.rs +5 -0
  129. data/vendor/crates/spikard-rb/src/runtime/server_runner.rs +326 -0
  130. data/vendor/crates/spikard-rb/src/server.rs +283 -0
  131. data/vendor/crates/spikard-rb/src/sse.rs +231 -0
  132. data/vendor/crates/spikard-rb/src/testing/client.rs +404 -0
  133. data/vendor/crates/spikard-rb/src/testing/mod.rs +7 -0
  134. data/vendor/crates/spikard-rb/src/testing/sse.rs +143 -0
  135. data/vendor/crates/spikard-rb/src/testing/websocket.rs +221 -0
  136. data/vendor/crates/spikard-rb/src/websocket.rs +233 -0
  137. data/vendor/crates/spikard-rb/tests/magnus_ffi_tests.rs +14 -0
  138. metadata +213 -0
data/sig/spikard.rbs ADDED
@@ -0,0 +1,366 @@
1
+ module Spikard
2
+ VERSION: String
3
+
4
+ # JSON-serializable value types
5
+ type jsonPrimitive = String | Integer | Float | bool | nil
6
+ type jsonValue = jsonPrimitive | Array[jsonValue] | Hash[String, jsonValue]
7
+ type jsonObject = Hash[String, jsonValue]
8
+ type jsonArray = Array[jsonValue]
9
+
10
+ module Background
11
+ def self.run: () { () -> void } -> void
12
+ end
13
+
14
+ class CompressionConfig
15
+ attr_accessor gzip: bool
16
+ attr_accessor brotli: bool
17
+ attr_accessor min_size: Integer
18
+ attr_accessor quality: Integer
19
+
20
+ def initialize: (?gzip: bool, ?brotli: bool, ?min_size: Integer, ?quality: Integer) -> void
21
+ end
22
+
23
+ class RateLimitConfig
24
+ attr_accessor per_second: Integer
25
+ attr_accessor burst: Integer
26
+ attr_accessor ip_based: bool
27
+
28
+ def initialize: (per_second: Integer, burst: Integer, ?ip_based: bool) -> void
29
+ end
30
+
31
+ class JwtConfig
32
+ attr_accessor secret: String
33
+ attr_accessor algorithm: String
34
+ attr_accessor audience: Array[String]?
35
+ attr_accessor issuer: String?
36
+ attr_accessor leeway: Integer
37
+
38
+ def initialize: (secret: String, ?algorithm: String, ?audience: Array[String]?, ?issuer: String?, ?leeway: Integer) -> void
39
+ end
40
+
41
+ class ApiKeyConfig
42
+ attr_accessor keys: Array[String]
43
+ attr_accessor header_name: String
44
+
45
+ def initialize: (keys: Array[String], ?header_name: String) -> void
46
+ end
47
+
48
+ class StaticFilesConfig
49
+ attr_accessor directory: String
50
+ attr_accessor route_prefix: String
51
+ attr_accessor index_file: bool
52
+ attr_accessor cache_control: String?
53
+
54
+ def initialize: (directory: String, route_prefix: String, ?index_file: bool, ?cache_control: String?) -> void
55
+ end
56
+
57
+ class ContactInfo
58
+ attr_accessor name: String?
59
+ attr_accessor email: String?
60
+ attr_accessor url: String?
61
+
62
+ def initialize: (?name: String?, ?email: String?, ?url: String?) -> void
63
+ end
64
+
65
+ class LicenseInfo
66
+ attr_accessor name: String
67
+ attr_accessor url: String?
68
+
69
+ def initialize: (name: String, ?url: String?) -> void
70
+ end
71
+
72
+ class ServerInfo
73
+ attr_accessor url: String
74
+ attr_accessor description: String?
75
+
76
+ def initialize: (url: String, ?description: String?) -> void
77
+ end
78
+
79
+ class SecuritySchemeInfo
80
+ attr_accessor type: String
81
+ attr_accessor scheme: String?
82
+ attr_accessor bearer_format: String?
83
+ attr_accessor location: String?
84
+ attr_accessor name: String?
85
+
86
+ def initialize: (type: String, ?scheme: String?, ?bearer_format: String?, ?location: String?, ?name: String?) -> void
87
+ end
88
+
89
+ class OpenApiConfig
90
+ attr_accessor enabled: bool
91
+ attr_accessor title: String
92
+ attr_accessor version: String
93
+ attr_accessor description: String?
94
+ attr_accessor swagger_ui_path: String
95
+ attr_accessor redoc_path: String
96
+ attr_accessor openapi_json_path: String
97
+ attr_accessor contact: ContactInfo?
98
+ attr_accessor license: LicenseInfo?
99
+ attr_accessor servers: Array[ServerInfo]
100
+ attr_accessor security_schemes: Hash[String, SecuritySchemeInfo]
101
+
102
+ def initialize: (
103
+ ?enabled: bool,
104
+ ?title: String,
105
+ ?version: String,
106
+ ?description: String?,
107
+ ?swagger_ui_path: String,
108
+ ?redoc_path: String,
109
+ ?openapi_json_path: String,
110
+ ?contact: ContactInfo?,
111
+ ?license: LicenseInfo?,
112
+ ?servers: Array[ServerInfo],
113
+ ?security_schemes: Hash[String, SecuritySchemeInfo]
114
+ ) -> void
115
+ end
116
+
117
+ class ServerConfig
118
+ attr_accessor host: String
119
+ attr_accessor port: Integer
120
+ attr_accessor workers: Integer
121
+ attr_accessor enable_request_id: bool
122
+ attr_accessor max_body_size: Integer?
123
+ attr_accessor request_timeout: Integer?
124
+ attr_accessor compression: CompressionConfig
125
+ attr_accessor rate_limit: RateLimitConfig?
126
+ attr_accessor jwt_auth: JwtConfig?
127
+ attr_accessor api_key_auth: ApiKeyConfig?
128
+ attr_accessor static_files: Array[StaticFilesConfig]
129
+ attr_accessor graceful_shutdown: bool
130
+ attr_accessor shutdown_timeout: Integer
131
+ attr_accessor openapi: OpenApiConfig?
132
+
133
+ def initialize: (
134
+ ?host: String,
135
+ ?port: Integer,
136
+ ?workers: Integer,
137
+ ?enable_request_id: bool,
138
+ ?max_body_size: Integer?,
139
+ ?request_timeout: Integer?,
140
+ ?compression: CompressionConfig,
141
+ ?rate_limit: RateLimitConfig?,
142
+ ?jwt_auth: JwtConfig?,
143
+ ?api_key_auth: ApiKeyConfig?,
144
+ ?static_files: Array[StaticFilesConfig],
145
+ ?graceful_shutdown: bool,
146
+ ?shutdown_timeout: Integer,
147
+ ?openapi: OpenApiConfig?
148
+ ) -> void
149
+ end
150
+
151
+ class Response
152
+ attr_reader content: jsonValue
153
+ attr_reader status_code: Integer
154
+ attr_reader headers: Hash[String, String]
155
+ attr_reader native_response: Object
156
+
157
+ def initialize: (
158
+ ?content: jsonValue,
159
+ ?body: jsonValue,
160
+ ?status_code: Integer,
161
+ ?headers: Hash[String, String]?,
162
+ ?content_type: String?
163
+ ) -> void
164
+ def status: () -> Integer
165
+ def status_code=: (Integer) -> Integer
166
+ def headers=: (Hash[String, String]?) -> Hash[String, String]
167
+ def content=: (jsonValue) -> jsonValue
168
+ def set_header: (String, String) -> String
169
+ def set_cookie: (
170
+ String,
171
+ String,
172
+ ?max_age: Integer?,
173
+ ?domain: String?,
174
+ ?path: String?,
175
+ ?secure: bool?,
176
+ ?httponly: bool?,
177
+ ?samesite: String?
178
+ ) -> String
179
+ def to_native_response: () -> Object
180
+ end
181
+
182
+ class StreamingResponse
183
+ attr_reader stream: Enumerable[String]
184
+ attr_reader status_code: Integer
185
+ attr_reader headers: Hash[String, String]
186
+ attr_reader native_response: Object
187
+
188
+ def initialize: (Enumerable[String], ?status_code: Integer, ?headers: Hash[String, String]?) -> void
189
+ def to_native_response: () -> Object
190
+ end
191
+
192
+ module Testing
193
+ end
194
+
195
+ module LifecycleHooks
196
+ def on_request: () { (Object) -> (Object | nil) } -> Proc
197
+ def pre_validation: () { (Object) -> (Object | nil) } -> Proc
198
+ def pre_handler: () { (Object) -> (Object | nil) } -> Proc
199
+ def on_response: () { (Response) -> (Response | nil) } -> Proc
200
+ def on_error: () { (Exception) -> (Response | nil) } -> Proc
201
+ end
202
+
203
+ module ProvideSupport
204
+ def provide: (
205
+ String | Symbol,
206
+ ?Object,
207
+ ?depends_on: Array[String | Symbol],
208
+ ?singleton: bool,
209
+ ?cacheable: bool
210
+ ) ?{ (**jsonObject) -> Object } -> self
211
+ def dependencies: () -> Spikard::Native::DependencyRegistry
212
+ end
213
+
214
+ class App
215
+ include LifecycleHooks
216
+ include ProvideSupport
217
+
218
+ attr_reader routes: Array[Array[String]]
219
+
220
+ def initialize: () -> void
221
+ def register_route: (String, String, ?handler_name: String?, **jsonObject) { (Object) -> (Response | jsonValue) } -> Proc
222
+ def get: (String, ?handler_name: String?, **jsonObject) { (Object) -> (Response | jsonValue) } -> Proc
223
+ def post: (String, ?handler_name: String?, **jsonObject) { (Object) -> (Response | jsonValue) } -> Proc
224
+ def put: (String, ?handler_name: String?, **jsonObject) { (Object) -> (Response | jsonValue) } -> Proc
225
+ def patch: (String, ?handler_name: String?, **jsonObject) { (Object) -> (Response | jsonValue) } -> Proc
226
+ def delete: (String, ?handler_name: String?, **jsonObject) { (Object) -> (Response | jsonValue) } -> Proc
227
+ def options: (String, ?handler_name: String?, **jsonObject) { (Object) -> (Response | jsonValue) } -> Proc
228
+ def head: (String, ?handler_name: String?, **jsonObject) { (Object) -> (Response | jsonValue) } -> Proc
229
+ def trace: (String, ?handler_name: String?, **jsonObject) { (Object) -> (Response | jsonValue) } -> Proc
230
+ def websocket: (String, ?handler_name: String?, **jsonObject) { () -> WebSocketHandler } -> Proc
231
+ def sse: (String, ?handler_name: String?, **jsonObject) { () -> SseEventProducer } -> Proc
232
+ def websocket_handlers: () -> Hash[String, Proc]
233
+ def sse_producers: () -> Hash[String, Proc]
234
+ def run: (?config: ServerConfig | Hash[Symbol, jsonValue]?, ?host: String?, ?port: Integer?) -> void
235
+ end
236
+
237
+ class WebSocketHandler
238
+ def handle_message: (jsonObject) -> jsonObject?
239
+ def on_connect: () -> void
240
+ def on_disconnect: () -> void
241
+ end
242
+
243
+ class SseEvent
244
+ attr_accessor data: jsonObject
245
+ attr_accessor event_type: String?
246
+ attr_accessor id: String?
247
+ attr_accessor retry_ms: Integer?
248
+
249
+ def initialize: (data: jsonObject, ?event_type: String?, ?id: String?, ?retry_ms: Integer?) -> void
250
+ def to_h: () -> Hash[Symbol, jsonValue]
251
+ end
252
+
253
+ class SseEventProducer
254
+ def next_event: () -> SseEvent?
255
+ def on_connect: () -> void
256
+ def on_disconnect: () -> void
257
+ end
258
+
259
+ module Schema
260
+ def self.extract_json_schema: (Object) -> jsonObject?
261
+ end
262
+
263
+ module Native
264
+ def self.run_server: (
265
+ String,
266
+ Hash[Symbol, Proc],
267
+ ServerConfig,
268
+ Hash[Symbol, Array[Proc]],
269
+ Hash[String, Proc],
270
+ Hash[String, Proc],
271
+ Hash[String, jsonObject]
272
+ ) -> void
273
+
274
+ class DependencyRegistry
275
+ def initialize: () -> void
276
+ end
277
+
278
+ class TestClient
279
+ def initialize: (
280
+ String,
281
+ Hash[Symbol, Proc],
282
+ ServerConfig,
283
+ Hash[String, Proc],
284
+ Hash[String, Proc]
285
+ ) -> void
286
+ def request: (String, String, Hash[Symbol, jsonValue]) -> Hash[Symbol, jsonValue]
287
+ def websocket: (String) -> Object
288
+ def sse: (String) -> Object
289
+ def close: () -> void
290
+ end
291
+ end
292
+
293
+ module Testing
294
+ def self.create_test_client: (App, ?config: ServerConfig?) -> Testing::TestClient
295
+
296
+ class Response
297
+ attr_reader status_code: Integer
298
+ attr_reader headers: Hash[String, String]
299
+ attr_reader body: String?
300
+
301
+ def initialize: (Hash[Symbol, jsonValue]) -> void
302
+ def status: () -> Integer
303
+ def body_bytes: () -> String
304
+ def body_text: () -> String?
305
+ def text: () -> String?
306
+ def json: () -> jsonValue
307
+ def bytes: () -> Array[Integer]
308
+ end
309
+
310
+ class TestClient
311
+ def initialize: (Spikard::Native::TestClient) -> void
312
+ def self.new: (App | Spikard::Native::TestClient, ?config: ServerConfig?) -> TestClient
313
+ def request: (String, String, **jsonObject) -> Response
314
+ def websocket: (String) -> WebSocketTestConnection
315
+ def sse: (String) -> SseStream
316
+ def close: () -> void
317
+ def get: (String, **jsonObject) -> Response
318
+ def post: (String, **jsonObject) -> Response
319
+ def put: (String, **jsonObject) -> Response
320
+ def patch: (String, **jsonObject) -> Response
321
+ def delete: (String, **jsonObject) -> Response
322
+ def head: (String, **jsonObject) -> Response
323
+ def options: (String, **jsonObject) -> Response
324
+ def trace: (String, **jsonObject) -> Response
325
+ end
326
+
327
+ class WebSocketTestConnection
328
+ def initialize: (Object) -> void
329
+ def send_text: (String) -> void
330
+ def send_json: (jsonValue) -> void
331
+ def receive_text: () -> String
332
+ def receive_json: () -> jsonValue
333
+ def receive_bytes: () -> String
334
+ def receive_message: () -> WebSocketMessage
335
+ def close: () -> void
336
+ end
337
+
338
+ class WebSocketMessage
339
+ def initialize: (Object) -> void
340
+ def as_text: () -> String
341
+ def as_json: () -> jsonValue
342
+ def as_binary: () -> String
343
+ def close?: () -> bool
344
+ end
345
+
346
+ class SseStream
347
+ def initialize: (Object) -> void
348
+ def body: () -> String?
349
+ def events: () -> Array[InlineSseEvent]
350
+ def events_as_json: () -> jsonArray
351
+ end
352
+
353
+ class SseEvent
354
+ def initialize: (Object) -> void
355
+ def data: () -> jsonValue
356
+ def as_json: () -> jsonValue
357
+ end
358
+
359
+ class InlineSseEvent
360
+ attr_reader data: String
361
+
362
+ def initialize: (String) -> void
363
+ def as_json: () -> jsonValue
364
+ end
365
+ end
366
+ end
@@ -0,0 +1,5 @@
1
+ [tools]
2
+ ruby = "3.4"
3
+
4
+ [env]
5
+ MAINTENANCE = "true"
@@ -0,0 +1,63 @@
1
+ [package]
2
+ name = "spikard-bindings-shared"
3
+ version = "0.4.0"
4
+ edition = "2024"
5
+ authors = ["Na'aman Hirschfeld <nhirschfeld@gmail.com>"]
6
+ license = "MIT"
7
+ repository = "https://github.com/Goldziher/spikard"
8
+ homepage = "https://github.com/Goldziher/spikard"
9
+
10
+ [dependencies]
11
+ serde = { version = "1.0", features = ["derive"] }
12
+ serde_json = "1.0"
13
+ axum = { version = "0.8", features = ["multipart", "ws"] }
14
+ tokio = { version = "1", features = ["full"] }
15
+ thiserror = "2.0"
16
+ spikard-core = { path = "../spikard-core", features = ["di"] }
17
+ spikard-http = { path = "../spikard-http", features = ["di"] }
18
+ tracing = "0.1"
19
+ http = "1.4"
20
+ http-body-util = "0.1"
21
+
22
+ [features]
23
+ default = []
24
+ python-support = ["pyo3", "pyo3-async-runtimes"]
25
+ node-support = ["napi", "napi-derive"]
26
+ ruby-support = ["magnus"]
27
+ php-support = ["ext-php-rs"]
28
+ wasm-support = ["wasm-bindgen"]
29
+
30
+ [dependencies.pyo3]
31
+ version = "0.27"
32
+ optional = true
33
+ features = ["abi3-py310"]
34
+
35
+ [dependencies.pyo3-async-runtimes]
36
+ version = "0.27"
37
+ optional = true
38
+ features = ["tokio-runtime"]
39
+
40
+ [dependencies.napi]
41
+ version = "3"
42
+ optional = true
43
+ default-features = false
44
+ features = ["napi9", "async"]
45
+
46
+ [dependencies.napi-derive]
47
+ version = "3"
48
+ optional = true
49
+
50
+ [dependencies.magnus]
51
+ version = "0.8.2"
52
+ optional = true
53
+
54
+ [dependencies.ext-php-rs]
55
+ version = "0.15"
56
+ optional = true
57
+
58
+ [dependencies.wasm-bindgen]
59
+ version = "0.2"
60
+ optional = true
61
+
62
+ [dev-dependencies]
63
+ pretty_assertions = "1.4"
@@ -0,0 +1,139 @@
1
+ //! Example of implementing ConfigSource for a language binding
2
+ //!
3
+ //! This example demonstrates how a language binding (e.g., Python, Node.js, Ruby, PHP)
4
+ //! would implement the `ConfigSource` trait to extract ServerConfig from language-specific objects.
5
+
6
+ use spikard_bindings_shared::{ConfigExtractor, ConfigSource};
7
+ use std::collections::HashMap;
8
+
9
+ /// Example: PyO3 Python dict wrapper
10
+ struct PyDictWrapper {
11
+ data: HashMap<String, String>,
12
+ }
13
+
14
+ impl PyDictWrapper {
15
+ fn new() -> Self {
16
+ Self { data: HashMap::new() }
17
+ }
18
+
19
+ fn insert(&mut self, key: &str, value: &str) {
20
+ self.data.insert(key.to_string(), value.to_string());
21
+ }
22
+ }
23
+
24
+ impl ConfigSource for PyDictWrapper {
25
+ fn get_bool(&self, key: &str) -> Option<bool> {
26
+ self.data.get(key).and_then(|v| match v.as_str() {
27
+ "true" | "True" => Some(true),
28
+ "false" | "False" => Some(false),
29
+ _ => v.parse().ok(),
30
+ })
31
+ }
32
+
33
+ fn get_u64(&self, key: &str) -> Option<u64> {
34
+ self.data.get(key).and_then(|v| v.parse().ok())
35
+ }
36
+
37
+ fn get_u16(&self, key: &str) -> Option<u16> {
38
+ self.data.get(key).and_then(|v| v.parse().ok())
39
+ }
40
+
41
+ fn get_string(&self, key: &str) -> Option<String> {
42
+ self.data.get(key).cloned()
43
+ }
44
+
45
+ fn get_vec_string(&self, key: &str) -> Option<Vec<String>> {
46
+ self.data
47
+ .get(key)
48
+ .map(|s| s.split(',').map(|item| item.trim().to_string()).collect())
49
+ }
50
+
51
+ fn get_nested(&self, _key: &str) -> Option<Box<dyn ConfigSource + '_>> {
52
+ // In a real binding, this would return a nested wrapper for nested objects
53
+ None
54
+ }
55
+
56
+ fn has_key(&self, key: &str) -> bool {
57
+ self.data.contains_key(key)
58
+ }
59
+ }
60
+
61
+ fn main() {
62
+ println!("=== ConfigExtractor Example ===\n");
63
+
64
+ // Example 1: Extract compression config
65
+ println!("1. Extracting compression configuration:");
66
+ let mut compression_config = PyDictWrapper::new();
67
+ compression_config.insert("gzip", "true");
68
+ compression_config.insert("brotli", "true");
69
+ compression_config.insert("min_size", "2048");
70
+ compression_config.insert("quality", "9");
71
+
72
+ match ConfigExtractor::extract_compression_config(&compression_config) {
73
+ Ok(config) => {
74
+ println!(" gzip: {}", config.gzip);
75
+ println!(" brotli: {}", config.brotli);
76
+ println!(" min_size: {}", config.min_size);
77
+ println!(" quality: {}\n", config.quality);
78
+ }
79
+ Err(e) => println!(" Error: {}\n", e),
80
+ }
81
+
82
+ // Example 2: Extract JWT auth config
83
+ println!("2. Extracting JWT authentication configuration:");
84
+ let mut jwt_config = PyDictWrapper::new();
85
+ jwt_config.insert("secret", "my-secret-key");
86
+ jwt_config.insert("algorithm", "HS256");
87
+ jwt_config.insert("leeway", "60");
88
+
89
+ match ConfigExtractor::extract_jwt_config(&jwt_config) {
90
+ Ok(config) => {
91
+ println!(" secret: [REDACTED]");
92
+ println!(" algorithm: {}", config.algorithm);
93
+ println!(" leeway: {}\n", config.leeway);
94
+ }
95
+ Err(e) => println!(" Error: {}\n", e),
96
+ }
97
+
98
+ // Example 3: Extract API Key auth config
99
+ println!("3. Extracting API Key authentication configuration:");
100
+ let mut api_key_config = PyDictWrapper::new();
101
+ api_key_config.insert("keys", "key1,key2,key3");
102
+ api_key_config.insert("header_name", "X-API-Key");
103
+
104
+ match ConfigExtractor::extract_api_key_config(&api_key_config) {
105
+ Ok(config) => {
106
+ println!(" keys: {:?}", config.keys);
107
+ println!(" header_name: {}\n", config.header_name);
108
+ }
109
+ Err(e) => println!(" Error: {}\n", e),
110
+ }
111
+
112
+ // Example 4: Extract rate limit config (with required fields)
113
+ println!("4. Extracting rate limit configuration:");
114
+ let mut rate_limit_config = PyDictWrapper::new();
115
+ rate_limit_config.insert("per_second", "100");
116
+ rate_limit_config.insert("burst", "20");
117
+ rate_limit_config.insert("ip_based", "true");
118
+
119
+ match ConfigExtractor::extract_rate_limit_config(&rate_limit_config) {
120
+ Ok(config) => {
121
+ println!(" per_second: {}", config.per_second);
122
+ println!(" burst: {}", config.burst);
123
+ println!(" ip_based: {}\n", config.ip_based);
124
+ }
125
+ Err(e) => println!(" Error: {}\n", e),
126
+ }
127
+
128
+ // Example 5: Extract rate limit with missing required field
129
+ println!("5. Testing error handling (missing 'burst' field):");
130
+ let rate_limit_config = PyDictWrapper::new();
131
+ // Only setting per_second, burst is required
132
+
133
+ match ConfigExtractor::extract_rate_limit_config(&rate_limit_config) {
134
+ Ok(_config) => println!(" Success (unexpected!)"),
135
+ Err(e) => println!(" Expected error: {}\n", e),
136
+ }
137
+
138
+ println!("=== Example Complete ===");
139
+ }