amritk-galaxy-staging 0.2.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 (148) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +13 -0
  3. data/LICENSE +201 -0
  4. data/README.md +169 -0
  5. data/SECURITY.md +19 -0
  6. data/SKILL.md +83 -0
  7. data/api.md +186 -0
  8. data/lib/amritk-galaxy-staging/client.rb +238 -0
  9. data/lib/amritk-galaxy-staging/errors.rb +227 -0
  10. data/lib/amritk-galaxy-staging/file_part.rb +58 -0
  11. data/lib/amritk-galaxy-staging/internal/transport/base_client.rb +588 -0
  12. data/lib/amritk-galaxy-staging/internal/transport/pooled_net_requester.rb +224 -0
  13. data/lib/amritk-galaxy-staging/internal/type/array_of.rb +164 -0
  14. data/lib/amritk-galaxy-staging/internal/type/base_model.rb +534 -0
  15. data/lib/amritk-galaxy-staging/internal/type/base_page.rb +55 -0
  16. data/lib/amritk-galaxy-staging/internal/type/boolean.rb +77 -0
  17. data/lib/amritk-galaxy-staging/internal/type/converter.rb +333 -0
  18. data/lib/amritk-galaxy-staging/internal/type/enum.rb +132 -0
  19. data/lib/amritk-galaxy-staging/internal/type/file_input.rb +111 -0
  20. data/lib/amritk-galaxy-staging/internal/type/hash_of.rb +186 -0
  21. data/lib/amritk-galaxy-staging/internal/type/request_parameters.rb +42 -0
  22. data/lib/amritk-galaxy-staging/internal/type/union.rb +238 -0
  23. data/lib/amritk-galaxy-staging/internal/type/unknown.rb +81 -0
  24. data/lib/amritk-galaxy-staging/internal/util.rb +965 -0
  25. data/lib/amritk-galaxy-staging/internal.rb +18 -0
  26. data/lib/amritk-galaxy-staging/models/authentication_create_token_params.rb +26 -0
  27. data/lib/amritk-galaxy-staging/models/authentication_create_user_params.rb +32 -0
  28. data/lib/amritk-galaxy-staging/models/authentication_list_me_params.rb +14 -0
  29. data/lib/amritk-galaxy-staging/models/celestial_body.rb +21 -0
  30. data/lib/amritk-galaxy-staging/models/celestial_body_create_params.rb +20 -0
  31. data/lib/amritk-galaxy-staging/models/new_planet_webhook_event.rb +9 -0
  32. data/lib/amritk-galaxy-staging/models/paginated_resource.rb +46 -0
  33. data/lib/amritk-galaxy-staging/models/planet.rb +221 -0
  34. data/lib/amritk-galaxy-staging/models/planet_create_params.rb +14 -0
  35. data/lib/amritk-galaxy-staging/models/planet_delete_params.rb +20 -0
  36. data/lib/amritk-galaxy-staging/models/planet_list_params.rb +30 -0
  37. data/lib/amritk-galaxy-staging/models/planet_list_response.rb +16 -0
  38. data/lib/amritk-galaxy-staging/models/planet_retrieve_params.rb +20 -0
  39. data/lib/amritk-galaxy-staging/models/planet_update_params.rb +20 -0
  40. data/lib/amritk-galaxy-staging/models/planet_upload_image_params.rb +29 -0
  41. data/lib/amritk-galaxy-staging/models/planet_upload_image_response.rb +48 -0
  42. data/lib/amritk-galaxy-staging/models/satellite.rb +96 -0
  43. data/lib/amritk-galaxy-staging/models/token.rb +18 -0
  44. data/lib/amritk-galaxy-staging/models/unwrap_webhook_event.rb +14 -0
  45. data/lib/amritk-galaxy-staging/models/user.rb +26 -0
  46. data/lib/amritk-galaxy-staging/models/webhook_unwrap_params.rb +14 -0
  47. data/lib/amritk-galaxy-staging/models.rb +87 -0
  48. data/lib/amritk-galaxy-staging/request_options.rb +79 -0
  49. data/lib/amritk-galaxy-staging/resources/authentication.rb +80 -0
  50. data/lib/amritk-galaxy-staging/resources/celestial_bodies.rb +36 -0
  51. data/lib/amritk-galaxy-staging/resources/planets.rb +215 -0
  52. data/lib/amritk-galaxy-staging/resources/webhooks.rb +37 -0
  53. data/lib/amritk-galaxy-staging/version.rb +5 -0
  54. data/lib/amritk-galaxy-staging.rb +79 -0
  55. data/rbi/amritk-galaxy-staging/client.rbi +130 -0
  56. data/rbi/amritk-galaxy-staging/errors.rbi +205 -0
  57. data/rbi/amritk-galaxy-staging/file_part.rbi +37 -0
  58. data/rbi/amritk-galaxy-staging/internal/transport/base_client.rbi +312 -0
  59. data/rbi/amritk-galaxy-staging/internal/transport/pooled_net_requester.rbi +84 -0
  60. data/rbi/amritk-galaxy-staging/internal/type/array_of.rbi +108 -0
  61. data/rbi/amritk-galaxy-staging/internal/type/base_model.rbi +319 -0
  62. data/rbi/amritk-galaxy-staging/internal/type/base_page.rbi +43 -0
  63. data/rbi/amritk-galaxy-staging/internal/type/boolean.rbi +59 -0
  64. data/rbi/amritk-galaxy-staging/internal/type/converter.rbi +225 -0
  65. data/rbi/amritk-galaxy-staging/internal/type/enum.rbi +82 -0
  66. data/rbi/amritk-galaxy-staging/internal/type/file_input.rbi +60 -0
  67. data/rbi/amritk-galaxy-staging/internal/type/hash_of.rbi +108 -0
  68. data/rbi/amritk-galaxy-staging/internal/type/request_parameters.rbi +33 -0
  69. data/rbi/amritk-galaxy-staging/internal/type/union.rbi +134 -0
  70. data/rbi/amritk-galaxy-staging/internal/type/unknown.rbi +59 -0
  71. data/rbi/amritk-galaxy-staging/internal/util.rbi +515 -0
  72. data/rbi/amritk-galaxy-staging/internal.rbi +18 -0
  73. data/rbi/amritk-galaxy-staging/models/authentication_create_token_params.rbi +46 -0
  74. data/rbi/amritk-galaxy-staging/models/authentication_create_user_params.rbi +54 -0
  75. data/rbi/amritk-galaxy-staging/models/authentication_list_me_params.rbi +34 -0
  76. data/rbi/amritk-galaxy-staging/models/celestial_body.rbi +21 -0
  77. data/rbi/amritk-galaxy-staging/models/celestial_body_create_params.rbi +53 -0
  78. data/rbi/amritk-galaxy-staging/models/new_planet_webhook_event.rbi +23 -0
  79. data/rbi/amritk-galaxy-staging/models/paginated_resource.rbi +93 -0
  80. data/rbi/amritk-galaxy-staging/models/planet.rbi +373 -0
  81. data/rbi/amritk-galaxy-staging/models/planet_create_params.rbi +34 -0
  82. data/rbi/amritk-galaxy-staging/models/planet_delete_params.rbi +41 -0
  83. data/rbi/amritk-galaxy-staging/models/planet_list_params.rbi +60 -0
  84. data/rbi/amritk-galaxy-staging/models/planet_list_response.rbi +33 -0
  85. data/rbi/amritk-galaxy-staging/models/planet_retrieve_params.rbi +41 -0
  86. data/rbi/amritk-galaxy-staging/models/planet_update_params.rbi +41 -0
  87. data/rbi/amritk-galaxy-staging/models/planet_upload_image_params.rbi +55 -0
  88. data/rbi/amritk-galaxy-staging/models/planet_upload_image_response.rbi +85 -0
  89. data/rbi/amritk-galaxy-staging/models/satellite.rbi +160 -0
  90. data/rbi/amritk-galaxy-staging/models/token.rbi +30 -0
  91. data/rbi/amritk-galaxy-staging/models/unwrap_webhook_event.rbi +20 -0
  92. data/rbi/amritk-galaxy-staging/models/user.rbi +36 -0
  93. data/rbi/amritk-galaxy-staging/models/webhook_unwrap_params.rbi +34 -0
  94. data/rbi/amritk-galaxy-staging/models.rbi +45 -0
  95. data/rbi/amritk-galaxy-staging/request_options.rbi +64 -0
  96. data/rbi/amritk-galaxy-staging/resources/authentication.rbi +49 -0
  97. data/rbi/amritk-galaxy-staging/resources/celestial_bodies.rbi +29 -0
  98. data/rbi/amritk-galaxy-staging/resources/planets.rbi +180 -0
  99. data/rbi/amritk-galaxy-staging/resources/webhooks.rbi +31 -0
  100. data/rbi/amritk-galaxy-staging/version.rbi +5 -0
  101. data/sig/amritk-galaxy-staging/client.rbs +62 -0
  102. data/sig/amritk-galaxy-staging/errors.rbs +117 -0
  103. data/sig/amritk-galaxy-staging/file_part.rbs +21 -0
  104. data/sig/amritk-galaxy-staging/internal/transport/base_client.rbs +133 -0
  105. data/sig/amritk-galaxy-staging/internal/transport/pooled_net_requester.rbs +48 -0
  106. data/sig/amritk-galaxy-staging/internal/type/array_of.rbs +48 -0
  107. data/sig/amritk-galaxy-staging/internal/type/base_model.rbs +102 -0
  108. data/sig/amritk-galaxy-staging/internal/type/base_page.rbs +24 -0
  109. data/sig/amritk-galaxy-staging/internal/type/boolean.rbs +26 -0
  110. data/sig/amritk-galaxy-staging/internal/type/converter.rbs +79 -0
  111. data/sig/amritk-galaxy-staging/internal/type/enum.rbs +32 -0
  112. data/sig/amritk-galaxy-staging/internal/type/file_input.rbs +25 -0
  113. data/sig/amritk-galaxy-staging/internal/type/hash_of.rbs +48 -0
  114. data/sig/amritk-galaxy-staging/internal/type/request_parameters.rbs +17 -0
  115. data/sig/amritk-galaxy-staging/internal/type/union.rbs +52 -0
  116. data/sig/amritk-galaxy-staging/internal/type/unknown.rbs +26 -0
  117. data/sig/amritk-galaxy-staging/internal/util.rbs +199 -0
  118. data/sig/amritk-galaxy-staging/internal.rbs +9 -0
  119. data/sig/amritk-galaxy-staging/models/authentication_create_token_params.rbs +28 -0
  120. data/sig/amritk-galaxy-staging/models/authentication_create_user_params.rbs +34 -0
  121. data/sig/amritk-galaxy-staging/models/authentication_list_me_params.rbs +17 -0
  122. data/sig/amritk-galaxy-staging/models/celestial_body.rbs +12 -0
  123. data/sig/amritk-galaxy-staging/models/celestial_body_create_params.rbs +24 -0
  124. data/sig/amritk-galaxy-staging/models/new_planet_webhook_event.rbs +11 -0
  125. data/sig/amritk-galaxy-staging/models/paginated_resource.rbs +53 -0
  126. data/sig/amritk-galaxy-staging/models/planet.rbs +210 -0
  127. data/sig/amritk-galaxy-staging/models/planet_create_params.rbs +17 -0
  128. data/sig/amritk-galaxy-staging/models/planet_delete_params.rbs +24 -0
  129. data/sig/amritk-galaxy-staging/models/planet_list_params.rbs +32 -0
  130. data/sig/amritk-galaxy-staging/models/planet_list_response.rbs +17 -0
  131. data/sig/amritk-galaxy-staging/models/planet_retrieve_params.rbs +24 -0
  132. data/sig/amritk-galaxy-staging/models/planet_update_params.rbs +26 -0
  133. data/sig/amritk-galaxy-staging/models/planet_upload_image_params.rbs +32 -0
  134. data/sig/amritk-galaxy-staging/models/planet_upload_image_response.rbs +50 -0
  135. data/sig/amritk-galaxy-staging/models/satellite.rbs +95 -0
  136. data/sig/amritk-galaxy-staging/models/token.rbs +15 -0
  137. data/sig/amritk-galaxy-staging/models/unwrap_webhook_event.rbs +11 -0
  138. data/sig/amritk-galaxy-staging/models/user.rbs +19 -0
  139. data/sig/amritk-galaxy-staging/models/webhook_unwrap_params.rbs +17 -0
  140. data/sig/amritk-galaxy-staging/models.rbs +39 -0
  141. data/sig/amritk-galaxy-staging/request_options.rbs +34 -0
  142. data/sig/amritk-galaxy-staging/resources/authentication.rbs +24 -0
  143. data/sig/amritk-galaxy-staging/resources/celestial_bodies.rbs +12 -0
  144. data/sig/amritk-galaxy-staging/resources/planets.rbs +68 -0
  145. data/sig/amritk-galaxy-staging/resources/webhooks.rbs +13 -0
  146. data/sig/amritk-galaxy-staging/version.rbs +3 -0
  147. data/sig/manifest.yaml +6 -0
  148. metadata +248 -0
@@ -0,0 +1,965 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DemoApiScalarGalaxy
4
+ module Internal
5
+ # @api private
6
+ module Util
7
+ # @api private
8
+ #
9
+ # @return [Float]
10
+ def self.monotonic_secs = Process.clock_gettime(Process::CLOCK_MONOTONIC)
11
+
12
+ # @api private
13
+ #
14
+ # @param ns [Module, Class]
15
+ #
16
+ # @return [Enumerable<Module, Class>]
17
+ def self.walk_namespaces(ns)
18
+ ns
19
+ .constants(false)
20
+ .lazy
21
+ .flat_map do
22
+ case (c = ns.const_get(_1, false))
23
+ in Module | Class
24
+ walk_namespaces(c)
25
+ else
26
+ []
27
+ end
28
+ end
29
+ .chain([ns])
30
+ end
31
+
32
+ class << self
33
+ # @api private
34
+ #
35
+ # @return [String]
36
+ def arch
37
+ case (arch = RbConfig::CONFIG["arch"])&.downcase
38
+ in nil
39
+ "unknown"
40
+ in /aarch64|arm64/
41
+ "arm64"
42
+ in /x86_64/
43
+ "x64"
44
+ in /arm/
45
+ "arm"
46
+ else
47
+ "other:#{arch}"
48
+ end
49
+ end
50
+
51
+ # @api private
52
+ #
53
+ # @return [String]
54
+ def os
55
+ case (host = RbConfig::CONFIG["host_os"])&.downcase
56
+ in nil
57
+ "Unknown"
58
+ in /linux/
59
+ "Linux"
60
+ in /darwin/
61
+ "MacOS"
62
+ in /freebsd/
63
+ "FreeBSD"
64
+ in /openbsd/
65
+ "OpenBSD"
66
+ in /mswin|mingw|cygwin|ucrt/
67
+ "Windows"
68
+ else
69
+ "Other:#{host}"
70
+ end
71
+ end
72
+ end
73
+
74
+ class << self
75
+ # @api private
76
+ #
77
+ # @param input [Object]
78
+ #
79
+ # @return [Boolean]
80
+ def primitive?(input)
81
+ case input
82
+ in true | false | Numeric | Symbol | String
83
+ true
84
+ else
85
+ false
86
+ end
87
+ end
88
+
89
+ # @api private
90
+ #
91
+ # @param input [String, Boolean]
92
+ #
93
+ # @return [Boolean, Object]
94
+ def coerce_boolean(input)
95
+ case input.is_a?(String) ? input.downcase : input
96
+ in "true"
97
+ true
98
+ in "false"
99
+ false
100
+ else
101
+ input
102
+ end
103
+ end
104
+
105
+ # @api private
106
+ #
107
+ # @param input [String, Boolean]
108
+ #
109
+ # @raise [ArgumentError]
110
+ # @return [Boolean, nil]
111
+ def coerce_boolean!(input)
112
+ case coerce_boolean(input)
113
+ in true | false | nil => coerced
114
+ coerced
115
+ else
116
+ raise ArgumentError.new("Unable to coerce #{input.inspect} into boolean value")
117
+ end
118
+ end
119
+
120
+ # @api private
121
+ #
122
+ # @param input [String, Integer]
123
+ #
124
+ # @return [Integer, Object]
125
+ def coerce_integer(input)
126
+ Integer(input, exception: false) || input
127
+ end
128
+
129
+ # @api private
130
+ #
131
+ # @param input [String, Integer, Float]
132
+ #
133
+ # @return [Float, Object]
134
+ def coerce_float(input)
135
+ Float(input, exception: false) || input
136
+ end
137
+
138
+ # @api private
139
+ #
140
+ # @param input [Object]
141
+ #
142
+ # @return [Hash{Object=>Object}, Object]
143
+ def coerce_hash(input)
144
+ case input
145
+ in NilClass | Array | Set | Enumerator | StringIO | IO
146
+ input
147
+ else
148
+ input.respond_to?(:to_h) ? input.to_h : input
149
+ end
150
+ end
151
+
152
+ # @api private
153
+ #
154
+ # @param input [Object]
155
+ #
156
+ # @raise [ArgumentError]
157
+ # @return [Hash{Object=>Object}, nil]
158
+ def coerce_hash!(input)
159
+ case coerce_hash(input)
160
+ in Hash | nil => coerced
161
+ coerced
162
+ else
163
+ message =
164
+ "Expected a #{Hash} or #{DemoApiScalarGalaxy::Internal::Type::BaseModel}, got #{input.inspect}"
165
+ raise ArgumentError.new(message)
166
+ end
167
+ end
168
+ end
169
+
170
+ class << self
171
+ # @api private
172
+ #
173
+ # @param lhs [Object]
174
+ # @param rhs [Object]
175
+ # @param concat [Boolean]
176
+ #
177
+ # @return [Object]
178
+ private def deep_merge_lr(lhs, rhs, concat: false)
179
+ case [lhs, rhs, concat]
180
+ in [Hash, Hash, _]
181
+ lhs.merge(rhs) { deep_merge_lr(_2, _3, concat: concat) }
182
+ in [Array, Array, true]
183
+ lhs.concat(rhs)
184
+ else
185
+ rhs
186
+ end
187
+ end
188
+
189
+ # @api private
190
+ #
191
+ # Recursively merge one hash with another. If the values at a given key are not
192
+ # both hashes, just take the new value.
193
+ #
194
+ # @param values [Array<Object>]
195
+ #
196
+ # @param sentinel [Object, nil] the value to return if no values are provided.
197
+ #
198
+ # @param concat [Boolean] whether to merge sequences by concatenation.
199
+ #
200
+ # @return [Object]
201
+ def deep_merge(*values, sentinel: nil, concat: false)
202
+ case values
203
+ in [value, *values]
204
+ values.reduce(value) { |acc, val| deep_merge_lr(acc, val, concat: concat) }
205
+ else
206
+ sentinel
207
+ end
208
+ end
209
+
210
+ # @api private
211
+ #
212
+ # @param data [Hash{Symbol=>Object}, Array<Object>, Object]
213
+ # @param pick [Symbol, Integer, Array<Symbol, Integer>, Proc, nil]
214
+ # @param blk [Proc, nil]
215
+ #
216
+ # @return [Object, nil]
217
+ def dig(data, pick, &blk)
218
+ case [data, pick]
219
+ in [_, nil]
220
+ data
221
+ in [Hash, Symbol] | [Array, Integer]
222
+ data.fetch(pick) { blk&.call }
223
+ in [Hash | Array, Array]
224
+ pick.reduce(data) do |acc, key|
225
+ case acc
226
+ in Hash if acc.key?(key)
227
+ acc.fetch(key)
228
+ in Array if key.is_a?(Integer) && key < acc.length
229
+ acc[key]
230
+ else
231
+ return blk&.call
232
+ end
233
+ end
234
+ in [_, Proc]
235
+ pick.call(data)
236
+ else
237
+ blk&.call
238
+ end
239
+ end
240
+ end
241
+
242
+ # @type [Regexp]
243
+ #
244
+ # https://www.rfc-editor.org/rfc/rfc3986.html#section-3.3
245
+ RFC_3986_NOT_PCHARS = /[^A-Za-z0-9\-._~!$&'()*+,;=:@]+/
246
+
247
+ class << self
248
+ # @api private
249
+ #
250
+ # @param uri [URI::Generic]
251
+ #
252
+ # @return [String]
253
+ def uri_origin(uri)
254
+ "#{uri.scheme}://#{uri.host}#{":#{uri.port}" unless uri.port == uri.default_port}"
255
+ end
256
+
257
+ # @api private
258
+ #
259
+ # @param path [String, Integer]
260
+ #
261
+ # @return [String]
262
+ def encode_path(path)
263
+ path
264
+ .to_s
265
+ .gsub(DemoApiScalarGalaxy::Internal::Util::RFC_3986_NOT_PCHARS) { ERB::Util.url_encode(_1) }
266
+ end
267
+
268
+ # @api private
269
+ #
270
+ # @param path [String, Array<String>]
271
+ #
272
+ # @return [String]
273
+ def interpolate_path(path)
274
+ case path
275
+ in String
276
+ path
277
+ in []
278
+ ""
279
+ in [String => p, *interpolations]
280
+ encoded = interpolations.map { encode_path(_1) }
281
+ format(p, *encoded)
282
+ end
283
+ end
284
+ end
285
+
286
+ class << self
287
+ # @api private
288
+ #
289
+ # @param query [String, nil]
290
+ #
291
+ # @return [Hash{String=>Array<String>}]
292
+ def decode_query(query)
293
+ CGI.parse(query.to_s)
294
+ end
295
+
296
+ # @api private
297
+ #
298
+ # @param query [Hash{String=>Array<String>, String, nil}, nil]
299
+ #
300
+ # @return [String, nil]
301
+ def encode_query(query)
302
+ query.to_h.empty? ? nil : URI.encode_www_form(query)
303
+ end
304
+ end
305
+
306
+ class << self
307
+ # @api private
308
+ #
309
+ # @param url [URI::Generic, String]
310
+ #
311
+ # @return [Hash{Symbol=>String, Integer, nil}]
312
+ def parse_uri(url)
313
+ parsed = URI::Generic.component.zip(URI.split(url)).to_h
314
+ {**parsed, query: decode_query(parsed.fetch(:query))}
315
+ end
316
+
317
+ # @api private
318
+ #
319
+ # @param parsed [Hash{Symbol=>String, Integer, nil}] .
320
+ #
321
+ # @option parsed [String, nil] :scheme
322
+ #
323
+ # @option parsed [String, nil] :host
324
+ #
325
+ # @option parsed [Integer, nil] :port
326
+ #
327
+ # @option parsed [String, nil] :path
328
+ #
329
+ # @option parsed [Hash{String=>Array<String>}] :query
330
+ #
331
+ # @return [URI::Generic]
332
+ def unparse_uri(parsed)
333
+ URI::Generic.build(**parsed, query: encode_query(parsed.fetch(:query)))
334
+ end
335
+
336
+ # @api private
337
+ #
338
+ # @param lhs [Hash{Symbol=>String, Integer, nil}] .
339
+ #
340
+ # @option lhs [String, nil] :scheme
341
+ #
342
+ # @option lhs [String, nil] :host
343
+ #
344
+ # @option lhs [Integer, nil] :port
345
+ #
346
+ # @option lhs [String, nil] :path
347
+ #
348
+ # @option lhs [Hash{String=>Array<String>}] :query
349
+ #
350
+ # @param rhs [Hash{Symbol=>String, Integer, nil}] .
351
+ #
352
+ # @option rhs [String, nil] :scheme
353
+ #
354
+ # @option rhs [String, nil] :host
355
+ #
356
+ # @option rhs [Integer, nil] :port
357
+ #
358
+ # @option rhs [String, nil] :path
359
+ #
360
+ # @option rhs [Hash{String=>Array<String>}] :query
361
+ #
362
+ # @return [URI::Generic]
363
+ def join_parsed_uri(lhs, rhs)
364
+ base_path, base_query = lhs.fetch_values(:path, :query)
365
+ slashed = base_path.end_with?("/") ? base_path : "#{base_path}/"
366
+
367
+ merged = {**parse_uri(rhs.fetch(:path)), **rhs.except(:path, :query)}
368
+ parsed_path, parsed_query = merged.fetch_values(:path, :query)
369
+ override = URI::Generic.build(**merged.slice(:scheme, :host, :port), path: parsed_path)
370
+
371
+ joined = URI.join(URI::Generic.build(lhs.except(:path, :query)), slashed, override)
372
+ query =
373
+ deep_merge(
374
+ joined.path == base_path ? base_query : {},
375
+ parsed_query,
376
+ rhs[:query].to_h,
377
+ concat: true
378
+ )
379
+
380
+ joined.query = encode_query(query)
381
+ joined
382
+ end
383
+ end
384
+
385
+ class << self
386
+ # @api private
387
+ #
388
+ # @param headers [Hash{String=>String, Integer, Array<String, Integer, nil>, nil}]
389
+ #
390
+ # @return [Hash{String=>String}]
391
+ def normalized_headers(*headers)
392
+ {}.merge(*headers.compact)
393
+ .to_h do |key, val|
394
+ value =
395
+ case val
396
+ in Array
397
+ val.filter_map { _1&.to_s&.strip }.join(", ")
398
+ else
399
+ val&.to_s&.strip
400
+ end
401
+ [key.downcase, value]
402
+ end
403
+ end
404
+ end
405
+
406
+ # @api private
407
+ #
408
+ # An adapter that satisfies the IO interface required by `::IO.copy_stream`
409
+ class ReadIOAdapter
410
+ # @api private
411
+ #
412
+ # @return [Boolean, nil]
413
+ def close? = @closing
414
+
415
+ # @api private
416
+ def close
417
+ case @stream
418
+ in Enumerator
419
+ DemoApiScalarGalaxy::Internal::Util.close_fused!(@stream)
420
+ in IO if close?
421
+ @stream.close
422
+ else
423
+ end
424
+ end
425
+
426
+ # @api private
427
+ #
428
+ # @param max_len [Integer, nil]
429
+ #
430
+ # @return [String]
431
+ private def read_enum(max_len)
432
+ case max_len
433
+ in nil
434
+ @stream.to_a.join
435
+ in Integer
436
+ @buf << @stream.next while @buf.length < max_len
437
+ @buf.slice!(..max_len)
438
+ end
439
+ rescue StopIteration
440
+ @stream = nil
441
+ @buf.slice!(0..)
442
+ end
443
+
444
+ # @api private
445
+ #
446
+ # @param max_len [Integer, nil]
447
+ # @param out_string [String, nil]
448
+ #
449
+ # @return [String, nil]
450
+ def read(max_len = nil, out_string = nil)
451
+ case @stream
452
+ in nil
453
+ nil
454
+ in IO | StringIO
455
+ @stream.read(max_len, out_string)
456
+ in Enumerator
457
+ read = read_enum(max_len)
458
+ case out_string
459
+ in String
460
+ out_string.replace(read)
461
+ in nil
462
+ read
463
+ end
464
+ end.tap(&@blk)
465
+ end
466
+
467
+ # @api private
468
+ #
469
+ # @param src [String, Pathname, StringIO, Enumerable<String>]
470
+ # @param blk [Proc]
471
+ #
472
+ # @yieldparam [String]
473
+ def initialize(src, &blk)
474
+ @stream =
475
+ case src
476
+ in String
477
+ StringIO.new(src)
478
+ in Pathname
479
+ @closing = true
480
+ src.open(binmode: true)
481
+ else
482
+ src
483
+ end
484
+ @buf = String.new
485
+ @blk = blk
486
+ end
487
+ end
488
+
489
+ class << self
490
+ # @param blk [Proc]
491
+ #
492
+ # @yieldparam [Enumerator::Yielder]
493
+ # @return [Enumerable<String>]
494
+ def writable_enum(&blk)
495
+ Enumerator.new do |y|
496
+ y.define_singleton_method(:write) do
497
+ self << _1.dup
498
+ _1.bytesize
499
+ end
500
+
501
+ blk.call(y)
502
+ end
503
+ end
504
+ end
505
+
506
+ # @type [Regexp]
507
+ JSON_CONTENT = %r{^application/(?:[a-zA-Z0-9.-]+\+)?json(?!l)}
508
+ # @type [Regexp]
509
+ JSONL_CONTENT = %r{^application/(:?x-(?:n|l)djson)|(:?(?:x-)?jsonl)}
510
+
511
+ class << self
512
+ # @api private
513
+ #
514
+ # @param query [Hash{Symbol=>Object}]
515
+ #
516
+ # @return [Hash{Symbol=>Object}]
517
+ def encode_query_params(query)
518
+ out = {}
519
+ query.each { write_query_param_element!(out, _1, _2) }
520
+ out
521
+ end
522
+
523
+ # @api private
524
+ #
525
+ # @param collection [Hash{Symbol=>Object}]
526
+ # @param key [String]
527
+ # @param element [Object]
528
+ #
529
+ # @return [nil]
530
+ private def write_query_param_element!(collection, key, element)
531
+ case element
532
+ in Hash
533
+ element.each { |name, value| write_query_param_element!(collection, "#{key}[#{name}]", value) }
534
+ in Array
535
+ collection[key] = element.map(&:to_s).join(",")
536
+ else
537
+ collection[key] = element.to_s
538
+ end
539
+ end
540
+
541
+ # @api private
542
+ #
543
+ # @param y [Enumerator::Yielder]
544
+ # @param val [Object]
545
+ # @param closing [Array<Proc>]
546
+ # @param content_type [String, nil]
547
+ private def write_multipart_content(y, val:, closing:, content_type: nil)
548
+ content_line = "Content-Type: %s\r\n\r\n"
549
+
550
+ case val
551
+ in DemoApiScalarGalaxy::FilePart
552
+ return (
553
+ write_multipart_content(y, val: val.content, closing: closing, content_type: val.content_type)
554
+ )
555
+ in Pathname
556
+ y << format(content_line, content_type || "application/octet-stream")
557
+ io = val.open(binmode: true)
558
+ closing << io.method(:close)
559
+ IO.copy_stream(io, y)
560
+ in IO
561
+ y << format(content_line, content_type || "application/octet-stream")
562
+ IO.copy_stream(val, y)
563
+ in StringIO
564
+ y << format(content_line, content_type || "application/octet-stream")
565
+ y << val.string
566
+ in -> { primitive?(_1) }
567
+ y << format(content_line, content_type || "text/plain")
568
+ y << val.to_s
569
+ else
570
+ y << format(content_line, content_type || "application/json")
571
+ y << JSON.generate(val)
572
+ end
573
+ y << "\r\n"
574
+ end
575
+
576
+ # @api private
577
+ #
578
+ # @param y [Enumerator::Yielder]
579
+ # @param boundary [String]
580
+ # @param key [Symbol, String]
581
+ # @param val [Object]
582
+ # @param closing [Array<Proc>]
583
+ private def write_multipart_chunk(y, boundary:, key:, val:, closing:)
584
+ y << "--#{boundary}\r\n"
585
+ y << "Content-Disposition: form-data"
586
+
587
+ y << "; name=\"#{key}\"" unless key.nil?
588
+
589
+ case val
590
+ in DemoApiScalarGalaxy::FilePart unless val.filename.nil?
591
+ filename = encode_path(val.filename)
592
+ y << "; filename=\"#{filename}\""
593
+ in Pathname | IO
594
+ filename = encode_path(::File.basename(val.to_path))
595
+ y << "; filename=\"#{filename}\""
596
+ else
597
+ end
598
+ y << "\r\n"
599
+
600
+ write_multipart_content(y, val: val, closing: closing)
601
+ end
602
+
603
+ # @api private
604
+ #
605
+ # https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.1.md#special-considerations-for-multipart-content
606
+ #
607
+ # @param body [Object]
608
+ #
609
+ # @return [Array(String, Enumerable<String>)]
610
+ private def encode_multipart_streaming(body)
611
+ # rubocop:disable Style/CaseEquality
612
+ # RFC 1521 Section 7.2.1 says we should have 70 char maximum for boundary length
613
+ boundary = SecureRandom.urlsafe_base64(46)
614
+
615
+ closing = []
616
+ strio =
617
+ writable_enum do |y|
618
+ case body
619
+ in Hash
620
+ body.each do |key, val|
621
+ case val
622
+ in Array if val.all? {
623
+ primitive?(_1) || DemoApiScalarGalaxy::Internal::Type::FileInput === _1
624
+ }
625
+ val.each do |v|
626
+ write_multipart_chunk(y, boundary: boundary, key: key, val: v, closing: closing)
627
+ end
628
+ else
629
+ write_multipart_chunk(y, boundary: boundary, key: key, val: val, closing: closing)
630
+ end
631
+ end
632
+ else
633
+ write_multipart_chunk(y, boundary: boundary, key: nil, val: body, closing: closing)
634
+ end
635
+ y << "--#{boundary}--\r\n"
636
+ end
637
+
638
+ fused_io = fused_enum(strio) { closing.each(&:call) }
639
+ [boundary, fused_io]
640
+ # rubocop:enable Style/CaseEquality
641
+ end
642
+
643
+ # @api private
644
+ #
645
+ # @param headers [Hash{String=>String}]
646
+ # @param body [Object]
647
+ #
648
+ # @return [Object]
649
+ def encode_content(headers, body)
650
+ # rubocop:disable Style/CaseEquality
651
+ content_type = headers["content-type"]
652
+ case [content_type, body]
653
+ in [DemoApiScalarGalaxy::Internal::Util::JSON_CONTENT, Hash | Array | -> { primitive?(_1) }]
654
+ [headers, JSON.generate(body)]
655
+ in [
656
+ DemoApiScalarGalaxy::Internal::Util::JSONL_CONTENT,
657
+ Enumerable
658
+ ] unless DemoApiScalarGalaxy::Internal::Type::FileInput === body
659
+ [headers, body.lazy.map { JSON.generate(_1) }]
660
+ in [%r{^multipart/form-data}, Hash | DemoApiScalarGalaxy::Internal::Type::FileInput]
661
+ boundary, strio = encode_multipart_streaming(body)
662
+ headers = {**headers, "content-type" => "#{content_type}; boundary=#{boundary}"}
663
+ [headers, strio]
664
+ in [_, Symbol | Numeric]
665
+ [headers, body.to_s]
666
+ in [_, StringIO]
667
+ [headers, body.string]
668
+ in [_, DemoApiScalarGalaxy::FilePart]
669
+ [headers, body.content]
670
+ else
671
+ [headers, body]
672
+ end
673
+ # rubocop:enable Style/CaseEquality
674
+ end
675
+
676
+ # @api private
677
+ #
678
+ # https://www.iana.org/assignments/character-sets/character-sets.xhtml
679
+ #
680
+ # @param content_type [String]
681
+ # @param text [String]
682
+ def force_charset!(content_type, text:)
683
+ charset = /charset=([^;\s]+)/.match(content_type)&.captures&.first
684
+
685
+ return unless charset
686
+
687
+ begin
688
+ encoding = Encoding.find(charset)
689
+ text.force_encoding(encoding)
690
+ rescue ArgumentError
691
+ nil
692
+ end
693
+ end
694
+
695
+ # @api private
696
+ #
697
+ # Assumes each chunk in stream has `Encoding::BINARY`.
698
+ #
699
+ # @param headers [Hash{String=>String}]
700
+ # @param stream [Enumerable<String>]
701
+ # @param suppress_error [Boolean]
702
+ #
703
+ # @raise [JSON::ParserError]
704
+ # @return [Object]
705
+ def decode_content(headers, stream:, suppress_error: false)
706
+ case (content_type = headers["content-type"])
707
+ in DemoApiScalarGalaxy::Internal::Util::JSON_CONTENT
708
+ return nil if (json = stream.to_a.join).empty?
709
+
710
+ begin
711
+ JSON.parse(json, symbolize_names: true)
712
+ rescue JSON::ParserError => e
713
+ raise e unless suppress_error
714
+ json
715
+ end
716
+ in DemoApiScalarGalaxy::Internal::Util::JSONL_CONTENT
717
+ lines = decode_lines(stream)
718
+ chain_fused(lines) do |y|
719
+ lines.each do
720
+ next if _1.empty?
721
+
722
+ y << JSON.parse(_1, symbolize_names: true)
723
+ end
724
+ end
725
+ in %r{^text/event-stream}
726
+ lines = decode_lines(stream)
727
+ decode_sse(lines)
728
+ else
729
+ text = stream.to_a.join
730
+ force_charset!(content_type, text: text)
731
+ StringIO.new(text)
732
+ end
733
+ end
734
+ end
735
+
736
+ class << self
737
+ # @api private
738
+ #
739
+ # https://doc.rust-lang.org/std/iter/trait.FusedIterator.html
740
+ #
741
+ # @param enum [Enumerable<Object>]
742
+ # @param external [Boolean]
743
+ # @param close [Proc]
744
+ #
745
+ # @return [Enumerable<Object>]
746
+ def fused_enum(enum, external: false, &close)
747
+ fused = false
748
+ iter =
749
+ Enumerator.new do |y|
750
+ next if fused
751
+
752
+ fused = true
753
+ external ? loop { y << enum.next } : enum.each(&y)
754
+ ensure
755
+ close&.call
756
+ close = nil
757
+ end
758
+
759
+ iter.define_singleton_method(:rewind) do
760
+ fused = true
761
+ self
762
+ end
763
+ iter
764
+ end
765
+
766
+ # @api private
767
+ #
768
+ # @param enum [Enumerable<Object>, nil]
769
+ def close_fused!(enum)
770
+ return unless enum.is_a?(Enumerator)
771
+
772
+ # rubocop:disable Lint/UnreachableLoop
773
+ enum.rewind.each { break }
774
+ # rubocop:enable Lint/UnreachableLoop
775
+ end
776
+
777
+ # @api private
778
+ #
779
+ # @param enum [Enumerable<Object>, nil]
780
+ # @param blk [Proc]
781
+ #
782
+ # @yieldparam [Enumerator::Yielder]
783
+ # @return [Enumerable<Object>]
784
+ def chain_fused(enum, &blk)
785
+ iter = Enumerator.new { blk.call(_1) }
786
+ fused_enum(iter) { close_fused!(enum) }
787
+ end
788
+ end
789
+
790
+ class << self
791
+ # @api private
792
+ #
793
+ # Assumes Strings have been forced into having `Encoding::BINARY`.
794
+ #
795
+ # This decoder is responsible for reassembling lines split across multiple
796
+ # fragments.
797
+ #
798
+ # @param enum [Enumerable<String>]
799
+ #
800
+ # @return [Enumerable<String>]
801
+ def decode_lines(enum)
802
+ re = /(\r\n|\r|\n)/
803
+ buffer = String.new
804
+ cr_seen = nil
805
+
806
+ chain_fused(enum) do |y|
807
+ enum.each do |row|
808
+ offset = buffer.bytesize
809
+ buffer << row
810
+ while (match = re.match(buffer, cr_seen&.to_i || offset))
811
+ case [match.captures.first, cr_seen]
812
+ in ["\r", nil]
813
+ cr_seen = match.end(1)
814
+ next
815
+ in ["\r" | "\r\n", Integer]
816
+ y << buffer.slice!(..(cr_seen.pred))
817
+ else
818
+ y << buffer.slice!(..(match.end(1).pred))
819
+ end
820
+ offset = 0
821
+ cr_seen = nil
822
+ end
823
+ end
824
+
825
+ y << buffer.slice!(..(cr_seen.pred)) unless cr_seen.nil?
826
+ y << buffer unless buffer.empty?
827
+ end
828
+ end
829
+
830
+ # @api private
831
+ #
832
+ # https://html.spec.whatwg.org/multipage/server-sent-events.html#parsing-an-event-stream
833
+ #
834
+ # Assumes that `lines` has been decoded with `#decode_lines`.
835
+ #
836
+ # @param lines [Enumerable<String>]
837
+ #
838
+ # @return [Enumerable<Hash{Symbol=>Object}>]
839
+ def decode_sse(lines)
840
+ # rubocop:disable Metrics/BlockLength
841
+ chain_fused(lines) do |y|
842
+ blank = {event: nil, data: nil, id: nil, retry: nil}
843
+ current = {}
844
+
845
+ lines.each do |line|
846
+ case line.sub(/\R$/, "")
847
+ in ""
848
+ next if current.empty?
849
+ y << {**blank, **current}
850
+ current = {}
851
+ in /^:/
852
+ next
853
+ in /^([^:]+):\s?(.*)$/
854
+ field, value = Regexp.last_match.captures
855
+ case field
856
+ in "event"
857
+ current.merge!(event: value)
858
+ in "data"
859
+ (current[:data] ||= String.new) << (value << "\n")
860
+ in "id" unless value.include?("\0")
861
+ current.merge!(id: value)
862
+ in "retry" if /^\d+$/ =~ value
863
+ current.merge!(retry: Integer(value))
864
+ else
865
+ end
866
+ else
867
+ end
868
+ end
869
+ # rubocop:enable Metrics/BlockLength
870
+
871
+ y << {**blank, **current} unless current.empty?
872
+ end
873
+ end
874
+ end
875
+
876
+ # @api private
877
+ module SorbetRuntimeSupport
878
+ class MissingSorbetRuntimeError < ::RuntimeError
879
+ end
880
+
881
+ # @api private
882
+ #
883
+ # @return [Hash{Symbol=>Object}]
884
+ private def sorbet_runtime_constants = @sorbet_runtime_constants ||= {}
885
+
886
+ # @api private
887
+ #
888
+ # @param name [Symbol]
889
+ def const_missing(name)
890
+ super unless sorbet_runtime_constants.key?(name)
891
+
892
+ unless Object.const_defined?(:T)
893
+ message = "Trying to access a Sorbet constant #{name.inspect} without `sorbet-runtime`."
894
+ raise MissingSorbetRuntimeError.new(message)
895
+ end
896
+
897
+ sorbet_runtime_constants.fetch(name).call
898
+ end
899
+
900
+ # @api private
901
+ #
902
+ # @param name [Symbol]
903
+ #
904
+ # @return [Boolean]
905
+ def sorbet_constant_defined?(name) = sorbet_runtime_constants.key?(name)
906
+
907
+ # @api private
908
+ #
909
+ # @param name [Symbol]
910
+ # @param blk [Proc]
911
+ def define_sorbet_constant!(name, &blk) = sorbet_runtime_constants.store(name, blk)
912
+
913
+ # @api private
914
+ #
915
+ # @return [Object]
916
+ def to_sorbet_type = raise NotImplementedError
917
+
918
+ class << self
919
+ # @api private
920
+ #
921
+ # @param type [DemoApiScalarGalaxy::Internal::Util::SorbetRuntimeSupport, Object]
922
+ #
923
+ # @return [Object]
924
+ def to_sorbet_type(type)
925
+ case type
926
+ in DemoApiScalarGalaxy::Internal::Util::SorbetRuntimeSupport
927
+ type.to_sorbet_type
928
+ in Class | Module
929
+ type
930
+ in true | false
931
+ T::Boolean
932
+ else
933
+ type.class
934
+ end
935
+ end
936
+ end
937
+ end
938
+
939
+ extend DemoApiScalarGalaxy::Internal::Util::SorbetRuntimeSupport
940
+
941
+ define_sorbet_constant!(:ParsedUri) do
942
+ T.type_alias do
943
+ {
944
+ scheme: T.nilable(String),
945
+ host: T.nilable(String),
946
+ port: T.nilable(Integer),
947
+ path: T.nilable(String),
948
+ query: T::Hash[String, T::Array[String]]
949
+ }
950
+ end
951
+ end
952
+
953
+ define_sorbet_constant!(:ServerSentEvent) do
954
+ T.type_alias do
955
+ {
956
+ event: T.nilable(String),
957
+ data: T.nilable(String),
958
+ id: T.nilable(String),
959
+ retry: T.nilable(Integer)
960
+ }
961
+ end
962
+ end
963
+ end
964
+ end
965
+ end