spikard 0.13.0 → 0.15.3

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 (207) hide show
  1. checksums.yaml +4 -4
  2. data/Steepfile +6 -0
  3. data/ext/spikard_rb/extconf.rb +1 -2
  4. data/ext/spikard_rb/{Cargo.lock → native/Cargo.lock} +818 -423
  5. data/ext/spikard_rb/native/Cargo.toml +24 -0
  6. data/ext/spikard_rb/src/lib.rs +5366 -3
  7. data/lib/spikard/native.rb +86 -0
  8. data/lib/spikard/version.rb +6 -1
  9. data/lib/spikard.rb +8 -52
  10. data/lib/spikard_rb.so +0 -0
  11. data/sig/types.rbs +427 -0
  12. metadata +14 -243
  13. data/LICENSE +0 -1
  14. data/README.md +0 -285
  15. data/ext/spikard_rb/Cargo.toml +0 -17
  16. data/lib/spikard/app.rb +0 -458
  17. data/lib/spikard/background.rb +0 -58
  18. data/lib/spikard/config.rb +0 -506
  19. data/lib/spikard/converters.rb +0 -13
  20. data/lib/spikard/grpc.rb +0 -232
  21. data/lib/spikard/handler_wrapper.rb +0 -113
  22. data/lib/spikard/provide.rb +0 -315
  23. data/lib/spikard/response.rb +0 -198
  24. data/lib/spikard/schema.rb +0 -243
  25. data/lib/spikard/sse.rb +0 -111
  26. data/lib/spikard/streaming_response.rb +0 -44
  27. data/lib/spikard/testing.rb +0 -474
  28. data/lib/spikard/upload_file.rb +0 -131
  29. data/lib/spikard/websocket.rb +0 -59
  30. data/sig/spikard.rbs +0 -739
  31. data/vendor/crates/spikard-bindings-shared/Cargo.toml +0 -75
  32. data/vendor/crates/spikard-bindings-shared/examples/config_extraction.rs +0 -132
  33. data/vendor/crates/spikard-bindings-shared/src/config_extractor.rs +0 -905
  34. data/vendor/crates/spikard-bindings-shared/src/conversion_traits.rs +0 -210
  35. data/vendor/crates/spikard-bindings-shared/src/di_traits.rs +0 -252
  36. data/vendor/crates/spikard-bindings-shared/src/error_response.rs +0 -404
  37. data/vendor/crates/spikard-bindings-shared/src/grpc_metadata.rs +0 -199
  38. data/vendor/crates/spikard-bindings-shared/src/handler_base.rs +0 -252
  39. data/vendor/crates/spikard-bindings-shared/src/json_conversion.rs +0 -829
  40. data/vendor/crates/spikard-bindings-shared/src/lazy_cache.rs +0 -587
  41. data/vendor/crates/spikard-bindings-shared/src/lib.rs +0 -33
  42. data/vendor/crates/spikard-bindings-shared/src/lifecycle_base.rs +0 -298
  43. data/vendor/crates/spikard-bindings-shared/src/lifecycle_executor.rs +0 -594
  44. data/vendor/crates/spikard-bindings-shared/src/response_builder.rs +0 -743
  45. data/vendor/crates/spikard-bindings-shared/src/response_interpreter.rs +0 -944
  46. data/vendor/crates/spikard-bindings-shared/src/test_client_base.rs +0 -260
  47. data/vendor/crates/spikard-bindings-shared/src/validation_helpers.rs +0 -369
  48. data/vendor/crates/spikard-bindings-shared/tests/config_extractor_behavior.rs +0 -192
  49. data/vendor/crates/spikard-bindings-shared/tests/error_response_edge_cases.rs +0 -383
  50. data/vendor/crates/spikard-bindings-shared/tests/full_coverage.rs +0 -459
  51. data/vendor/crates/spikard-bindings-shared/tests/handler_base_integration.rs +0 -280
  52. data/vendor/crates/spikard-bindings-shared/tests/integration_tests.rs +0 -669
  53. data/vendor/crates/spikard-core/Cargo.toml +0 -55
  54. data/vendor/crates/spikard-core/src/bindings/mod.rs +0 -3
  55. data/vendor/crates/spikard-core/src/bindings/response.rs +0 -130
  56. data/vendor/crates/spikard-core/src/debug.rs +0 -127
  57. data/vendor/crates/spikard-core/src/di/container.rs +0 -711
  58. data/vendor/crates/spikard-core/src/di/dependency.rs +0 -273
  59. data/vendor/crates/spikard-core/src/di/error.rs +0 -118
  60. data/vendor/crates/spikard-core/src/di/factory.rs +0 -548
  61. data/vendor/crates/spikard-core/src/di/graph.rs +0 -507
  62. data/vendor/crates/spikard-core/src/di/mod.rs +0 -192
  63. data/vendor/crates/spikard-core/src/di/resolved.rs +0 -428
  64. data/vendor/crates/spikard-core/src/di/value.rs +0 -282
  65. data/vendor/crates/spikard-core/src/errors.rs +0 -72
  66. data/vendor/crates/spikard-core/src/http.rs +0 -492
  67. data/vendor/crates/spikard-core/src/lib.rs +0 -29
  68. data/vendor/crates/spikard-core/src/lifecycle.rs +0 -1273
  69. data/vendor/crates/spikard-core/src/metadata.rs +0 -378
  70. data/vendor/crates/spikard-core/src/parameters.rs +0 -2546
  71. data/vendor/crates/spikard-core/src/problem.rs +0 -358
  72. data/vendor/crates/spikard-core/src/request_data.rs +0 -1146
  73. data/vendor/crates/spikard-core/src/router.rs +0 -530
  74. data/vendor/crates/spikard-core/src/schema_registry.rs +0 -197
  75. data/vendor/crates/spikard-core/src/type_hints.rs +0 -311
  76. data/vendor/crates/spikard-core/src/validation/error_mapper.rs +0 -710
  77. data/vendor/crates/spikard-core/src/validation/mod.rs +0 -470
  78. data/vendor/crates/spikard-core/tests/bindings_response_tests.rs +0 -136
  79. data/vendor/crates/spikard-core/tests/di_dependency_defaults.rs +0 -37
  80. data/vendor/crates/spikard-core/tests/error_mapper.rs +0 -761
  81. data/vendor/crates/spikard-core/tests/parameters_edge_cases.rs +0 -106
  82. data/vendor/crates/spikard-core/tests/parameters_full.rs +0 -701
  83. data/vendor/crates/spikard-core/tests/parameters_schema_and_formats.rs +0 -301
  84. data/vendor/crates/spikard-core/tests/request_data_roundtrip.rs +0 -67
  85. data/vendor/crates/spikard-core/tests/validation_coverage.rs +0 -250
  86. data/vendor/crates/spikard-core/tests/validation_error_paths.rs +0 -45
  87. data/vendor/crates/spikard-http/Cargo.toml +0 -82
  88. data/vendor/crates/spikard-http/examples/sse-notifications.rs +0 -148
  89. data/vendor/crates/spikard-http/examples/websocket-chat.rs +0 -92
  90. data/vendor/crates/spikard-http/src/auth.rs +0 -301
  91. data/vendor/crates/spikard-http/src/background.rs +0 -1859
  92. data/vendor/crates/spikard-http/src/bindings/mod.rs +0 -3
  93. data/vendor/crates/spikard-http/src/bindings/response.rs +0 -1
  94. data/vendor/crates/spikard-http/src/body_metadata.rs +0 -8
  95. data/vendor/crates/spikard-http/src/cors.rs +0 -1026
  96. data/vendor/crates/spikard-http/src/debug.rs +0 -128
  97. data/vendor/crates/spikard-http/src/di_handler.rs +0 -1672
  98. data/vendor/crates/spikard-http/src/grpc/framing.rs +0 -653
  99. data/vendor/crates/spikard-http/src/grpc/handler.rs +0 -1211
  100. data/vendor/crates/spikard-http/src/grpc/mod.rs +0 -556
  101. data/vendor/crates/spikard-http/src/grpc/service.rs +0 -706
  102. data/vendor/crates/spikard-http/src/grpc/streaming.rs +0 -319
  103. data/vendor/crates/spikard-http/src/handler_response.rs +0 -901
  104. data/vendor/crates/spikard-http/src/handler_trait.rs +0 -1015
  105. data/vendor/crates/spikard-http/src/handler_trait_tests.rs +0 -290
  106. data/vendor/crates/spikard-http/src/jsonrpc/http_handler.rs +0 -502
  107. data/vendor/crates/spikard-http/src/jsonrpc/method_registry.rs +0 -648
  108. data/vendor/crates/spikard-http/src/jsonrpc/mod.rs +0 -60
  109. data/vendor/crates/spikard-http/src/jsonrpc/openrpc.rs +0 -325
  110. data/vendor/crates/spikard-http/src/jsonrpc/protocol.rs +0 -1207
  111. data/vendor/crates/spikard-http/src/jsonrpc/router.rs +0 -2262
  112. data/vendor/crates/spikard-http/src/lib.rs +0 -566
  113. data/vendor/crates/spikard-http/src/lifecycle/adapter.rs +0 -230
  114. data/vendor/crates/spikard-http/src/lifecycle.rs +0 -1193
  115. data/vendor/crates/spikard-http/src/middleware/mod.rs +0 -560
  116. data/vendor/crates/spikard-http/src/middleware/multipart.rs +0 -912
  117. data/vendor/crates/spikard-http/src/middleware/urlencoded.rs +0 -513
  118. data/vendor/crates/spikard-http/src/middleware/validation.rs +0 -768
  119. data/vendor/crates/spikard-http/src/openapi/mod.rs +0 -309
  120. data/vendor/crates/spikard-http/src/openapi/parameter_extraction.rs +0 -535
  121. data/vendor/crates/spikard-http/src/openapi/schema_conversion.rs +0 -1363
  122. data/vendor/crates/spikard-http/src/openapi/spec_generation.rs +0 -667
  123. data/vendor/crates/spikard-http/src/query_parser.rs +0 -793
  124. data/vendor/crates/spikard-http/src/response.rs +0 -720
  125. data/vendor/crates/spikard-http/src/server/fast_router.rs +0 -186
  126. data/vendor/crates/spikard-http/src/server/grpc_routing.rs +0 -1243
  127. data/vendor/crates/spikard-http/src/server/handler.rs +0 -1661
  128. data/vendor/crates/spikard-http/src/server/lifecycle_execution.rs +0 -253
  129. data/vendor/crates/spikard-http/src/server/mod.rs +0 -1717
  130. data/vendor/crates/spikard-http/src/server/request_extraction.rs +0 -871
  131. data/vendor/crates/spikard-http/src/server/routing_factory.rs +0 -618
  132. data/vendor/crates/spikard-http/src/sse.rs +0 -1409
  133. data/vendor/crates/spikard-http/src/testing/form.rs +0 -52
  134. data/vendor/crates/spikard-http/src/testing/multipart.rs +0 -64
  135. data/vendor/crates/spikard-http/src/testing/test_client.rs +0 -825
  136. data/vendor/crates/spikard-http/src/testing.rs +0 -617
  137. data/vendor/crates/spikard-http/src/websocket.rs +0 -1477
  138. data/vendor/crates/spikard-http/tests/auth_integration.rs +0 -645
  139. data/vendor/crates/spikard-http/tests/background_behavior.rs +0 -832
  140. data/vendor/crates/spikard-http/tests/common/grpc_helpers.rs +0 -1012
  141. data/vendor/crates/spikard-http/tests/common/handlers.rs +0 -309
  142. data/vendor/crates/spikard-http/tests/common/mod.rs +0 -33
  143. data/vendor/crates/spikard-http/tests/common/test_builders.rs +0 -628
  144. data/vendor/crates/spikard-http/tests/di_handler_error_responses.rs +0 -162
  145. data/vendor/crates/spikard-http/tests/di_integration.rs +0 -192
  146. data/vendor/crates/spikard-http/tests/doc_snippets.rs +0 -5
  147. data/vendor/crates/spikard-http/tests/grpc_bidirectional_streaming.rs +0 -430
  148. data/vendor/crates/spikard-http/tests/grpc_client_streaming.rs +0 -738
  149. data/vendor/crates/spikard-http/tests/grpc_error_handling_test.rs +0 -652
  150. data/vendor/crates/spikard-http/tests/grpc_integration_test.rs +0 -334
  151. data/vendor/crates/spikard-http/tests/grpc_metadata_test.rs +0 -532
  152. data/vendor/crates/spikard-http/tests/grpc_server_integration.rs +0 -495
  153. data/vendor/crates/spikard-http/tests/grpc_server_streaming.rs +0 -975
  154. data/vendor/crates/spikard-http/tests/lifecycle_execution.rs +0 -1093
  155. data/vendor/crates/spikard-http/tests/middleware_stack_integration.rs +0 -389
  156. data/vendor/crates/spikard-http/tests/multipart_behavior.rs +0 -656
  157. data/vendor/crates/spikard-http/tests/request_extraction_full.rs +0 -513
  158. data/vendor/crates/spikard-http/tests/server_auth_middleware_behavior.rs +0 -328
  159. data/vendor/crates/spikard-http/tests/server_config_builder.rs +0 -335
  160. data/vendor/crates/spikard-http/tests/server_configured_router_behavior.rs +0 -374
  161. data/vendor/crates/spikard-http/tests/server_cors_preflight.rs +0 -83
  162. data/vendor/crates/spikard-http/tests/server_handler_wrappers.rs +0 -464
  163. data/vendor/crates/spikard-http/tests/server_method_router_additional_behavior.rs +0 -286
  164. data/vendor/crates/spikard-http/tests/server_method_router_coverage.rs +0 -118
  165. data/vendor/crates/spikard-http/tests/server_middleware_behavior.rs +0 -99
  166. data/vendor/crates/spikard-http/tests/server_middleware_branches.rs +0 -204
  167. data/vendor/crates/spikard-http/tests/server_openapi_jsonrpc_static.rs +0 -427
  168. data/vendor/crates/spikard-http/tests/server_router_behavior.rs +0 -121
  169. data/vendor/crates/spikard-http/tests/sse_behavior.rs +0 -620
  170. data/vendor/crates/spikard-http/tests/sse_full_behavior.rs +0 -584
  171. data/vendor/crates/spikard-http/tests/sse_handler_behavior.rs +0 -130
  172. data/vendor/crates/spikard-http/tests/test_client_requests.rs +0 -167
  173. data/vendor/crates/spikard-http/tests/testing_helpers.rs +0 -87
  174. data/vendor/crates/spikard-http/tests/testing_module_coverage.rs +0 -155
  175. data/vendor/crates/spikard-http/tests/urlencoded_content_type.rs +0 -82
  176. data/vendor/crates/spikard-http/tests/websocket_behavior.rs +0 -663
  177. data/vendor/crates/spikard-http/tests/websocket_full_behavior.rs +0 -440
  178. data/vendor/crates/spikard-http/tests/websocket_integration.rs +0 -150
  179. data/vendor/crates/spikard-rb/Cargo.toml +0 -63
  180. data/vendor/crates/spikard-rb/build.rs +0 -200
  181. data/vendor/crates/spikard-rb/src/background.rs +0 -63
  182. data/vendor/crates/spikard-rb/src/config/mod.rs +0 -5
  183. data/vendor/crates/spikard-rb/src/config/server_config.rs +0 -401
  184. data/vendor/crates/spikard-rb/src/conversion.rs +0 -688
  185. data/vendor/crates/spikard-rb/src/di/builder.rs +0 -100
  186. data/vendor/crates/spikard-rb/src/di/mod.rs +0 -410
  187. data/vendor/crates/spikard-rb/src/grpc/handler.rs +0 -875
  188. data/vendor/crates/spikard-rb/src/grpc/mod.rs +0 -13
  189. data/vendor/crates/spikard-rb/src/gvl.rs +0 -80
  190. data/vendor/crates/spikard-rb/src/handler.rs +0 -699
  191. data/vendor/crates/spikard-rb/src/integration/mod.rs +0 -3
  192. data/vendor/crates/spikard-rb/src/lib.rs +0 -2268
  193. data/vendor/crates/spikard-rb/src/lifecycle.rs +0 -334
  194. data/vendor/crates/spikard-rb/src/metadata/mod.rs +0 -5
  195. data/vendor/crates/spikard-rb/src/metadata/route_extraction.rs +0 -507
  196. data/vendor/crates/spikard-rb/src/request.rs +0 -439
  197. data/vendor/crates/spikard-rb/src/runtime/mod.rs +0 -5
  198. data/vendor/crates/spikard-rb/src/runtime/server_runner.rs +0 -368
  199. data/vendor/crates/spikard-rb/src/server.rs +0 -304
  200. data/vendor/crates/spikard-rb/src/sse.rs +0 -231
  201. data/vendor/crates/spikard-rb/src/testing/client.rs +0 -698
  202. data/vendor/crates/spikard-rb/src/testing/mod.rs +0 -7
  203. data/vendor/crates/spikard-rb/src/testing/sse.rs +0 -108
  204. data/vendor/crates/spikard-rb/src/testing/websocket.rs +0 -573
  205. data/vendor/crates/spikard-rb/src/websocket.rs +0 -521
  206. data/vendor/crates/spikard-rb-macros/Cargo.toml +0 -20
  207. data/vendor/crates/spikard-rb-macros/src/lib.rs +0 -51
@@ -1,506 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Spikard
4
- # Compression configuration for response compression middleware.
5
- #
6
- # Spikard supports gzip and brotli compression for responses.
7
- # Compression is applied based on Accept-Encoding headers.
8
- #
9
- # @example
10
- # compression = CompressionConfig.new(
11
- # gzip: true,
12
- # brotli: true,
13
- # min_size: 1024,
14
- # quality: 6
15
- # )
16
- class CompressionConfig
17
- attr_accessor :gzip, :brotli, :min_size, :quality
18
-
19
- # @param gzip [Boolean] Enable gzip compression (default: true)
20
- # @param brotli [Boolean] Enable brotli compression (default: true)
21
- # @param min_size [Integer] Minimum response size in bytes to compress (default: 1024)
22
- # @param quality [Integer] Compression quality level (0-11 for brotli, 0-9 for gzip, default: 6)
23
- def initialize(gzip: true, brotli: true, min_size: 1024, quality: 6)
24
- @gzip = normalize_boolean('gzip', gzip)
25
- @brotli = normalize_boolean('brotli', brotli)
26
- @min_size = normalize_nonnegative_integer('min_size', min_size)
27
- @quality = normalize_quality(quality)
28
- end
29
-
30
- private
31
-
32
- def normalize_boolean(name, value)
33
- return value if [true, false].include?(value)
34
-
35
- raise ArgumentError, "#{name} must be a boolean"
36
- end
37
-
38
- def normalize_nonnegative_integer(name, value)
39
- raise ArgumentError, "#{name} must be an Integer" unless value.is_a?(Integer)
40
- return value if value >= 0
41
-
42
- raise ArgumentError, "#{name} must be >= 0"
43
- end
44
-
45
- def normalize_quality(value)
46
- raise ArgumentError, 'quality must be a number' unless value.is_a?(Integer) || value.is_a?(Float)
47
-
48
- normalized = value.to_i
49
- return normalized if normalized.between?(0, 11)
50
-
51
- raise ArgumentError, 'quality must be between 0 and 11'
52
- end
53
- end
54
-
55
- # Rate limiting configuration using Generic Cell Rate Algorithm (GCRA).
56
- #
57
- # By default, rate limits are applied per IP address.
58
- #
59
- # @example
60
- # rate_limit = RateLimitConfig.new(
61
- # per_second: 100,
62
- # burst: 200,
63
- # ip_based: true
64
- # )
65
- class RateLimitConfig
66
- attr_accessor :per_second, :burst, :ip_based
67
-
68
- # @param per_second [Integer] Maximum requests per second
69
- # @param burst [Integer] Burst allowance - allows temporary spikes
70
- # @param ip_based [Boolean] Apply rate limits per IP address (default: true)
71
- def initialize(per_second:, burst:, ip_based: true)
72
- @per_second = per_second
73
- @burst = burst
74
- @ip_based = ip_based
75
- end
76
- end
77
-
78
- # JWT authentication configuration.
79
- #
80
- # Validates JWT tokens using the specified secret and algorithm.
81
- # Tokens are expected in the Authorization header as "Bearer <token>".
82
- #
83
- # Supported algorithms:
84
- # - HS256, HS384, HS512 (HMAC with SHA)
85
- # - RS256, RS384, RS512 (RSA signatures)
86
- # - ES256, ES384, ES512 (ECDSA signatures)
87
- # - PS256, PS384, PS512 (RSA-PSS signatures)
88
- #
89
- # @example
90
- # jwt = JwtConfig.new(
91
- # secret: 'your-secret-key',
92
- # algorithm: 'HS256',
93
- # audience: ['api.example.com'],
94
- # issuer: 'auth.example.com',
95
- # leeway: 30
96
- # )
97
- class JwtConfig
98
- attr_accessor :secret, :algorithm, :audience, :issuer, :leeway
99
-
100
- # @param secret [String] Secret key for JWT validation
101
- # @param algorithm [String] JWT algorithm (default: "HS256")
102
- # @param audience [Array<String>, nil] Expected audience claim(s)
103
- # @param issuer [String, nil] Expected issuer claim
104
- # @param leeway [Integer] Time leeway in seconds for exp/nbf/iat claims (default: 0)
105
- def initialize(secret:, algorithm: 'HS256', audience: nil, issuer: nil, leeway: 0)
106
- @secret = secret
107
- @algorithm = algorithm
108
- @audience = audience
109
- @issuer = issuer
110
- @leeway = leeway
111
- end
112
- end
113
-
114
- # API key authentication configuration.
115
- #
116
- # Validates API keys from request headers. Keys are matched exactly.
117
- #
118
- # @example
119
- # api_key = ApiKeyConfig.new(
120
- # keys: ['key-1', 'key-2', 'key-3'],
121
- # header_name: 'X-API-Key'
122
- # )
123
- class ApiKeyConfig
124
- attr_accessor :keys, :header_name
125
-
126
- # @param keys [Array<String>] List of valid API keys
127
- # @param header_name [String] HTTP header name to check for API key (default: "X-API-Key")
128
- def initialize(keys:, header_name: 'X-API-Key')
129
- @keys = keys
130
- @header_name = header_name
131
- end
132
- end
133
-
134
- # Static file serving configuration.
135
- #
136
- # Serves files from a directory at a given route prefix.
137
- # Multiple static file configurations can be registered.
138
- #
139
- # @example
140
- # static = StaticFilesConfig.new(
141
- # directory: './public',
142
- # route_prefix: '/static',
143
- # index_file: true,
144
- # cache_control: 'public, max-age=3600'
145
- # )
146
- class StaticFilesConfig
147
- attr_accessor :directory, :route_prefix, :index_file, :cache_control
148
-
149
- # @param directory [String] Directory path containing static files
150
- # @param route_prefix [String] URL prefix for serving static files (e.g., "/static")
151
- # @param index_file [Boolean] Serve index.html for directory requests (default: true)
152
- # @param cache_control [String, nil] Optional Cache-Control header value (e.g., "public, max-age=3600")
153
- def initialize(directory:, route_prefix:, index_file: true, cache_control: nil)
154
- @directory = directory
155
- @route_prefix = route_prefix
156
- @index_file = index_file
157
- @cache_control = cache_control
158
- end
159
- end
160
-
161
- # Contact information for OpenAPI documentation.
162
- #
163
- # @example
164
- # contact = ContactInfo.new(
165
- # name: 'API Team',
166
- # email: 'api@example.com',
167
- # url: 'https://example.com'
168
- # )
169
- class ContactInfo
170
- attr_accessor :name, :email, :url
171
-
172
- # @param name [String, nil] Name of the contact person/organization
173
- # @param email [String, nil] Email address for contact
174
- # @param url [String, nil] URL for contact information
175
- def initialize(name: nil, email: nil, url: nil)
176
- @name = name
177
- @email = email
178
- @url = url
179
- end
180
- end
181
-
182
- # License information for OpenAPI documentation.
183
- #
184
- # @example
185
- # license = LicenseInfo.new(
186
- # name: 'MIT',
187
- # url: 'https://opensource.org/licenses/MIT'
188
- # )
189
- class LicenseInfo
190
- attr_accessor :name, :url
191
-
192
- # @param name [String] License name (e.g., "MIT", "Apache 2.0")
193
- # @param url [String, nil] URL to the full license text
194
- def initialize(name:, url: nil)
195
- @name = name
196
- @url = url
197
- end
198
- end
199
-
200
- # Server information for OpenAPI documentation.
201
- #
202
- # Multiple servers can be specified for different environments.
203
- #
204
- # @example
205
- # server = ServerInfo.new(
206
- # url: 'https://api.example.com',
207
- # description: 'Production'
208
- # )
209
- class ServerInfo
210
- attr_accessor :url, :description
211
-
212
- # @param url [String] Server URL (e.g., "https://api.example.com")
213
- # @param description [String, nil] Description of the server (e.g., "Production", "Staging")
214
- def initialize(url:, description: nil)
215
- @url = url
216
- @description = description
217
- end
218
- end
219
-
220
- # Security scheme configuration for OpenAPI documentation.
221
- #
222
- # Supports HTTP (Bearer/JWT) and API Key authentication schemes.
223
- #
224
- # @example HTTP Bearer
225
- # scheme = SecuritySchemeInfo.new(
226
- # type: 'http',
227
- # scheme: 'bearer',
228
- # bearer_format: 'JWT'
229
- # )
230
- #
231
- # @example API Key
232
- # scheme = SecuritySchemeInfo.new(
233
- # type: 'apiKey',
234
- # location: 'header',
235
- # name: 'X-API-Key'
236
- # )
237
- class SecuritySchemeInfo
238
- attr_accessor :type, :scheme, :bearer_format, :location, :name
239
-
240
- # @param type [String] Security scheme type ("http" or "apiKey")
241
- # @param scheme [String, nil] HTTP scheme (e.g., "bearer", "basic") - for type="http"
242
- # @param bearer_format [String, nil] Format hint for Bearer tokens (e.g., "JWT") - for type="http"
243
- # @param location [String, nil] Where to look for the API key ("header", "query", or "cookie") - for type="apiKey"
244
- # @param name [String, nil] Parameter name (e.g., "X-API-Key") - for type="apiKey"
245
- def initialize(type:, scheme: nil, bearer_format: nil, location: nil, name: nil)
246
- @type = type
247
- @scheme = scheme
248
- @bearer_format = bearer_format
249
- @location = location
250
- @name = name
251
-
252
- validate!
253
- end
254
-
255
- private
256
-
257
- def validate!
258
- case @type
259
- when 'http'
260
- raise ArgumentError, 'scheme is required for type="http"' if @scheme.nil?
261
- when 'apiKey'
262
- raise ArgumentError, 'location and name are required for type="apiKey"' if @location.nil? || @name.nil?
263
- else
264
- raise ArgumentError, "type must be 'http' or 'apiKey', got: #{@type.inspect}"
265
- end
266
- end
267
- end
268
-
269
- # OpenAPI 3.1.0 documentation configuration.
270
- #
271
- # Spikard can automatically generate OpenAPI documentation from your routes.
272
- # When enabled, it serves:
273
- # - Swagger UI at /docs (customizable)
274
- # - Redoc at /redoc (customizable)
275
- # - OpenAPI JSON spec at /openapi.json (customizable)
276
- #
277
- # Security schemes are auto-detected from middleware configuration.
278
- # Schemas are generated from your route type hints and validation.
279
- #
280
- # @example
281
- # openapi = OpenApiConfig.new(
282
- # enabled: true,
283
- # title: 'My API',
284
- # version: '1.0.0',
285
- # description: 'A great API built with Spikard',
286
- # contact: ContactInfo.new(
287
- # name: 'API Team',
288
- # email: 'api@example.com',
289
- # url: 'https://example.com'
290
- # ),
291
- # license: LicenseInfo.new(
292
- # name: 'MIT',
293
- # url: 'https://opensource.org/licenses/MIT'
294
- # ),
295
- # servers: [
296
- # ServerInfo.new(url: 'https://api.example.com', description: 'Production'),
297
- # ServerInfo.new(url: 'http://localhost:8000', description: 'Development')
298
- # ]
299
- # )
300
- class OpenApiConfig
301
- attr_accessor :enabled, :title, :version, :description,
302
- :swagger_ui_path, :redoc_path, :openapi_json_path,
303
- :contact, :license, :servers, :security_schemes
304
-
305
- # @param enabled [Boolean] Enable OpenAPI generation (default: false for zero overhead)
306
- # @param title [String] API title (default: "API")
307
- # @param version [String] API version (default: "1.0.0")
308
- # @param description [String, nil] API description (supports Markdown)
309
- # @param swagger_ui_path [String] Path to serve Swagger UI (default: "/docs")
310
- # @param redoc_path [String] Path to serve Redoc (default: "/redoc")
311
- # @param openapi_json_path [String] Path to serve OpenAPI JSON spec (default: "/openapi.json")
312
- # @param contact [ContactInfo, nil] Contact information for the API
313
- # @param license [LicenseInfo, nil] License information for the API
314
- # @param servers [Array<ServerInfo>] List of server URLs for different environments (default: [])
315
- # @param security_schemes [Hash<String, SecuritySchemeInfo>] Custom security schemes (auto-detected if not provided)
316
- def initialize(
317
- enabled: false,
318
- title: 'API',
319
- version: '1.0.0',
320
- description: nil,
321
- swagger_ui_path: '/docs',
322
- redoc_path: '/redoc',
323
- openapi_json_path: '/openapi.json',
324
- contact: nil,
325
- license: nil,
326
- servers: [],
327
- security_schemes: {}
328
- )
329
- @enabled = enabled
330
- @title = title
331
- @version = version
332
- @description = description
333
- @swagger_ui_path = swagger_ui_path
334
- @redoc_path = redoc_path
335
- @openapi_json_path = openapi_json_path
336
- @contact = contact
337
- @license = license
338
- @servers = servers
339
- @security_schemes = security_schemes
340
- end
341
- end
342
-
343
- # JSON-RPC endpoint configuration.
344
- class JsonRpcConfig
345
- attr_accessor :enabled, :endpoint_path, :enable_batch, :max_batch_size
346
-
347
- # @param enabled [Boolean] Enable JSON-RPC endpoint registration (default: true)
348
- # @param endpoint_path [String] JSON-RPC endpoint path (default: "/rpc")
349
- # @param enable_batch [Boolean] Enable JSON-RPC batch support (default: true)
350
- # @param max_batch_size [Integer] Maximum batch size (default: 100)
351
- def initialize(enabled: true, endpoint_path: '/rpc', enable_batch: true, max_batch_size: 100)
352
- @enabled = normalize_boolean('enabled', enabled)
353
- @endpoint_path = endpoint_path
354
- @enable_batch = normalize_boolean('enable_batch', enable_batch)
355
- @max_batch_size = normalize_positive_integer('max_batch_size', max_batch_size)
356
- end
357
-
358
- private
359
-
360
- def normalize_boolean(name, value)
361
- return value if [true, false].include?(value)
362
-
363
- raise ArgumentError, "#{name} must be a boolean"
364
- end
365
-
366
- def normalize_positive_integer(name, value)
367
- raise ArgumentError, "#{name} must be an Integer" unless value.is_a?(Integer)
368
- return value if value.positive?
369
-
370
- raise ArgumentError, "#{name} must be > 0"
371
- end
372
- end
373
-
374
- # Complete server configuration for Spikard.
375
- #
376
- # This is the main configuration object that controls all aspects of the server
377
- # including network settings, middleware, authentication, and more.
378
- #
379
- # @example
380
- # config = ServerConfig.new(
381
- # host: '0.0.0.0',
382
- # port: 8080,
383
- # workers: 4,
384
- # compression: CompressionConfig.new(quality: 9),
385
- # rate_limit: RateLimitConfig.new(per_second: 100, burst: 200),
386
- # static_files: [
387
- # StaticFilesConfig.new(
388
- # directory: './public',
389
- # route_prefix: '/static'
390
- # )
391
- # ],
392
- # openapi: OpenApiConfig.new(
393
- # enabled: true,
394
- # title: 'My API',
395
- # version: '1.0.0'
396
- # )
397
- # )
398
- class ServerConfig
399
- attr_accessor :host, :port, :workers,
400
- :enable_request_id, :max_body_size, :request_timeout,
401
- :compression, :rate_limit, :jwt_auth, :api_key_auth,
402
- :static_files, :graceful_shutdown, :shutdown_timeout,
403
- :openapi, :jsonrpc
404
-
405
- # @param host [String] Host address to bind to (default: "127.0.0.1")
406
- # @param port [Integer] Port number to listen on (default: 8000, range: 1-65535)
407
- # @param workers [Integer] Number of Tokio worker threads (default: 1)
408
- # @param enable_request_id [Boolean] Add X-Request-ID header to responses (default: true)
409
- # @param max_body_size [Integer, nil] Maximum request body size in bytes (default: 10MB, nil for unlimited)
410
- # @param request_timeout [Integer, nil] Request timeout in seconds (default: 30, nil for no timeout)
411
- # @param compression [CompressionConfig, nil] Response compression configuration (default: enabled with defaults)
412
- # @param rate_limit [RateLimitConfig, nil] Rate limiting configuration (default: nil/disabled)
413
- # @param jwt_auth [JwtConfig, nil] JWT authentication configuration (default: nil/disabled)
414
- # @param api_key_auth [ApiKeyConfig, nil] API key authentication configuration (default: nil/disabled)
415
- # @param static_files [Array<StaticFilesConfig>] List of static file serving configurations (default: [])
416
- # @param graceful_shutdown [Boolean] Enable graceful shutdown (default: true)
417
- # @param shutdown_timeout [Integer] Graceful shutdown timeout in seconds (default: 30)
418
- # @param openapi [OpenApiConfig, nil] OpenAPI configuration (default: nil/disabled)
419
- # @param jsonrpc [JsonRpcConfig, nil] JSON-RPC configuration (default: nil/disabled)
420
- def initialize(
421
- host: '127.0.0.1',
422
- port: 8000,
423
- workers: 1,
424
- enable_request_id: true,
425
- max_body_size: 10 * 1024 * 1024, # 10MB
426
- request_timeout: 30,
427
- compression: CompressionConfig.new,
428
- rate_limit: nil,
429
- jwt_auth: nil,
430
- api_key_auth: nil,
431
- static_files: [],
432
- graceful_shutdown: true,
433
- shutdown_timeout: 30,
434
- openapi: nil,
435
- jsonrpc: nil
436
- )
437
- @host = host
438
- @port = normalize_port(port)
439
- @workers = normalize_workers(workers)
440
- @enable_request_id = normalize_boolean('enable_request_id', enable_request_id)
441
- @max_body_size = normalize_optional_nonnegative_integer('max_body_size', max_body_size)
442
- @request_timeout = normalize_timeout('request_timeout', request_timeout)
443
- @compression = compression
444
- @rate_limit = rate_limit
445
- @jwt_auth = jwt_auth
446
- @api_key_auth = api_key_auth
447
- @static_files = normalize_static_files(static_files)
448
- @graceful_shutdown = normalize_boolean('graceful_shutdown', graceful_shutdown)
449
- @shutdown_timeout = normalize_timeout('shutdown_timeout', shutdown_timeout)
450
- @openapi = openapi
451
- @jsonrpc = jsonrpc
452
- end
453
-
454
- private
455
-
456
- def normalize_port(port)
457
- raise ArgumentError, 'port must be an Integer' unless port.is_a?(Integer)
458
- return port if port.between?(1, 65_535)
459
-
460
- raise ArgumentError, 'port must be between 1 and 65535'
461
- end
462
-
463
- def normalize_workers(workers)
464
- raise ArgumentError, 'workers must be an Integer' unless workers.is_a?(Integer)
465
- return workers if workers >= 1
466
-
467
- raise ArgumentError, 'workers must be >= 1'
468
- end
469
-
470
- def normalize_boolean(name, value)
471
- return value if [true, false].include?(value)
472
-
473
- raise ArgumentError, "#{name} must be a boolean"
474
- end
475
-
476
- def normalize_optional_nonnegative_integer(name, value)
477
- return nil if value.nil?
478
- raise ArgumentError, "#{name} must be an Integer" unless value.is_a?(Integer)
479
- return value if value >= 0
480
-
481
- raise ArgumentError, "#{name} must be >= 0"
482
- end
483
-
484
- def normalize_timeout(name, value)
485
- return nil if value.nil?
486
- raise ArgumentError, "#{name} must be a number" unless value.is_a?(Integer) || value.is_a?(Float)
487
-
488
- normalized = value.to_i
489
- return normalized if normalized >= 0
490
-
491
- raise ArgumentError, "#{name} must be >= 0"
492
- end
493
-
494
- def normalize_static_files(static_files)
495
- return [] if static_files.nil?
496
- raise ArgumentError, 'static_files must be an Array' unless static_files.is_a?(Array)
497
-
498
- static_files.each do |entry|
499
- next if entry.is_a?(StaticFilesConfig)
500
-
501
- raise ArgumentError, 'static_files entries must be StaticFilesConfig'
502
- end
503
- static_files
504
- end
505
- end
506
- end
@@ -1,13 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Spikard
4
- # Conversion helpers between native Rust values and Ruby types.
5
- module Converters
6
- module_function
7
-
8
- # No-op conversion now that Rust materialises UploadFile.
9
- def convert_handler_body(body)
10
- body
11
- end
12
- end
13
- end