clerk-sdk-ruby 5.1.0 → 5.1.2

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 (219) hide show
  1. checksums.yaml +4 -4
  2. data/lib/clerk/actortokens.rb +1 -1
  3. data/lib/clerk/allowlistidentifiers.rb +1 -1
  4. data/lib/clerk/api_keys.rb +4 -4
  5. data/lib/clerk/betafeatures.rb +6 -6
  6. data/lib/clerk/billing.rb +3 -3
  7. data/lib/clerk/blocklistidentifiers.rb +1 -1
  8. data/lib/clerk/clients.rb +3 -3
  9. data/lib/clerk/domains.rb +2 -2
  10. data/lib/clerk/emailaddresses.rb +2 -2
  11. data/lib/clerk/emailandsmstemplates.rb +3 -3
  12. data/lib/clerk/emailsmstemplates.rb +9 -9
  13. data/lib/clerk/instancesettings.rb +7 -7
  14. data/lib/clerk/invitations.rb +3 -3
  15. data/lib/clerk/jwttemplates.rb +2 -2
  16. data/lib/clerk/m2m.rb +9 -9
  17. data/lib/clerk/machines.rb +4 -4
  18. data/lib/clerk/models/components/actortoken.rb +4 -4
  19. data/lib/clerk/models/components/allowlistidentifier.rb +6 -6
  20. data/lib/clerk/models/components/allowlistidentifier_object.rb +1 -1
  21. data/lib/clerk/models/components/billingpaymentattempt.rb +2 -2
  22. data/lib/clerk/models/components/billingpriceresponse.rb +1 -1
  23. data/lib/clerk/models/components/billingstatement.rb +2 -2
  24. data/lib/clerk/models/components/blocklistidentifier.rb +6 -6
  25. data/lib/clerk/models/components/blocklistidentifier_object.rb +1 -1
  26. data/lib/clerk/models/components/blocklistidentifiers.rb +1 -1
  27. data/lib/clerk/models/components/client.rb +7 -7
  28. data/lib/clerk/models/components/client_object.rb +1 -1
  29. data/lib/clerk/models/components/cnametarget.rb +1 -1
  30. data/lib/clerk/models/components/commercepayerresponse.rb +1 -1
  31. data/lib/clerk/models/components/commercepaymentmethodresponse.rb +3 -3
  32. data/lib/clerk/models/components/commerceplan.rb +3 -3
  33. data/lib/clerk/models/components/commercepricetransitiondetails.rb +2 -2
  34. data/lib/clerk/models/components/commercepricetransitionresponse.rb +1 -1
  35. data/lib/clerk/models/components/commercesubscription.rb +2 -2
  36. data/lib/clerk/models/components/commercesubscriptionitem.rb +4 -4
  37. data/lib/clerk/models/components/commercesubscriptionitem_plan.rb +3 -3
  38. data/lib/clerk/models/components/cookies.rb +1 -1
  39. data/lib/clerk/models/components/creator_role.rb +1 -1
  40. data/lib/clerk/models/components/default_role.rb +1 -1
  41. data/lib/clerk/models/components/domain.rb +2 -2
  42. data/lib/clerk/models/components/domains.rb +1 -1
  43. data/lib/clerk/models/components/emailaddress.rb +5 -5
  44. data/lib/clerk/models/components/emailaddress_admin.rb +3 -3
  45. data/lib/clerk/models/components/emailaddress_object.rb +1 -1
  46. data/lib/clerk/models/components/emailaddress_otp.rb +3 -3
  47. data/lib/clerk/models/components/emailaddress_saml.rb +3 -3
  48. data/lib/clerk/models/components/emailaddress_ticket.rb +3 -3
  49. data/lib/clerk/models/components/emaillink.rb +3 -3
  50. data/lib/clerk/models/components/enterpriseaccount.rb +5 -5
  51. data/lib/clerk/models/components/enterpriseaccount_oauth.rb +2 -2
  52. data/lib/clerk/models/components/enterpriseaccount_object.rb +1 -1
  53. data/lib/clerk/models/components/enterpriseaccount_saml.rb +3 -3
  54. data/lib/clerk/models/components/enterpriseaccount_ticket.rb +3 -3
  55. data/lib/clerk/models/components/enterpriseconnection_1.rb +3 -3
  56. data/lib/clerk/models/components/enterpriseconnection_2.rb +3 -3
  57. data/lib/clerk/models/components/extendfreetrialrequest.rb +1 -1
  58. data/lib/clerk/models/components/externalaccountwithverification.rb +7 -7
  59. data/lib/clerk/models/components/externalaccountwithverification_oauth.rb +2 -2
  60. data/lib/clerk/models/components/featureresponse.rb +1 -1
  61. data/lib/clerk/models/components/fromoauth.rb +2 -2
  62. data/lib/clerk/models/components/googleonetap.rb +3 -3
  63. data/lib/clerk/models/components/group.rb +1 -1
  64. data/lib/clerk/models/components/instance.rb +1 -1
  65. data/lib/clerk/models/components/instanceprotect.rb +1 -1
  66. data/lib/clerk/models/components/instancerestrictions.rb +1 -1
  67. data/lib/clerk/models/components/instancesettings.rb +1 -1
  68. data/lib/clerk/models/components/invitation.rb +5 -5
  69. data/lib/clerk/models/components/invitation_revoked.rb +5 -5
  70. data/lib/clerk/models/components/jwttemplate.rb +3 -3
  71. data/lib/clerk/models/components/machine.rb +1 -1
  72. data/lib/clerk/models/components/machine_created.rb +1 -1
  73. data/lib/clerk/models/components/machine_deleted.rb +1 -1
  74. data/lib/clerk/models/components/machine_secretkey.rb +1 -1
  75. data/lib/clerk/models/components/machinescope.rb +1 -1
  76. data/lib/clerk/models/components/machinescope_deleted.rb +1 -1
  77. data/lib/clerk/models/components/machinewithoutscopedmachines.rb +1 -1
  78. data/lib/clerk/models/components/oauthaccesstoken.rb +1 -1
  79. data/lib/clerk/models/components/oauthapplication.rb +6 -6
  80. data/lib/clerk/models/components/oauthapplications.rb +1 -1
  81. data/lib/clerk/models/components/oauthapplicationwithsecret.rb +7 -7
  82. data/lib/clerk/models/components/organization.rb +5 -5
  83. data/lib/clerk/models/components/organizationdomain.rb +3 -3
  84. data/lib/clerk/models/components/organizationdomain_object.rb +1 -1
  85. data/lib/clerk/models/components/organizationdomain_verification.rb +1 -1
  86. data/lib/clerk/models/components/organizationdomains.rb +1 -1
  87. data/lib/clerk/models/components/organizationinvitation.rb +2 -2
  88. data/lib/clerk/models/components/organizationinvitation_object.rb +1 -1
  89. data/lib/clerk/models/components/organizationinvitations.rb +1 -1
  90. data/lib/clerk/models/components/organizationinvitationswithpublicorganizationdata.rb +1 -1
  91. data/lib/clerk/models/components/organizationinvitationwithpublicorganizationdata.rb +2 -2
  92. data/lib/clerk/models/components/organizationinvitationwithpublicorganizationdata_object.rb +1 -1
  93. data/lib/clerk/models/components/organizationmembership.rb +2 -2
  94. data/lib/clerk/models/components/organizationmembership_object.rb +1 -1
  95. data/lib/clerk/models/components/organizationmembership_organization.rb +5 -5
  96. data/lib/clerk/models/components/organizationmembershippublicuserdata.rb +1 -1
  97. data/lib/clerk/models/components/organizationmemberships.rb +1 -1
  98. data/lib/clerk/models/components/organizations.rb +1 -1
  99. data/lib/clerk/models/components/organizationsettings.rb +3 -3
  100. data/lib/clerk/models/components/organizationwithlogo.rb +6 -6
  101. data/lib/clerk/models/components/passkey.rb +3 -3
  102. data/lib/clerk/models/components/passkey_object.rb +1 -1
  103. data/lib/clerk/models/components/payer.rb +1 -1
  104. data/lib/clerk/models/components/payment_source.rb +3 -3
  105. data/lib/clerk/models/components/permission.rb +3 -3
  106. data/lib/clerk/models/components/permissions.rb +1 -1
  107. data/lib/clerk/models/components/phonenumber.rb +4 -4
  108. data/lib/clerk/models/components/phonenumber_admin.rb +3 -3
  109. data/lib/clerk/models/components/phonenumber_object.rb +1 -1
  110. data/lib/clerk/models/components/phonenumber_otp.rb +3 -3
  111. data/lib/clerk/models/components/protocol.rb +1 -1
  112. data/lib/clerk/models/components/proxycheck.rb +4 -4
  113. data/lib/clerk/models/components/redirecturl.rb +3 -3
  114. data/lib/clerk/models/components/role.rb +3 -3
  115. data/lib/clerk/models/components/role_set_migration.rb +1 -1
  116. data/lib/clerk/models/components/roles.rb +1 -1
  117. data/lib/clerk/models/components/roleset.rb +2 -2
  118. data/lib/clerk/models/components/rolesetitem.rb +1 -1
  119. data/lib/clerk/models/components/samlaccount.rb +3 -3
  120. data/lib/clerk/models/components/samlaccount_object.rb +1 -1
  121. data/lib/clerk/models/components/samlaccount_saml.rb +3 -3
  122. data/lib/clerk/models/components/samlaccount_ticket.rb +3 -3
  123. data/lib/clerk/models/components/samlconnection_1.rb +3 -3
  124. data/lib/clerk/models/components/samlconnection_2.rb +3 -3
  125. data/lib/clerk/models/components/samlconnections.rb +1 -1
  126. data/lib/clerk/models/components/schemas_commerceplan.rb +1 -1
  127. data/lib/clerk/models/components/schemas_commercesubscriptionitem.rb +3 -3
  128. data/lib/clerk/models/components/schemas_commercesubscriptionitem_plan.rb +1 -1
  129. data/lib/clerk/models/components/schemas_featureresponse.rb +1 -1
  130. data/lib/clerk/models/components/schemas_samlconnection_samlconnection_1.rb +4 -4
  131. data/lib/clerk/models/components/schemas_samlconnection_samlconnection_2.rb +4 -4
  132. data/lib/clerk/models/components/session.rb +7 -7
  133. data/lib/clerk/models/components/session_object.rb +1 -1
  134. data/lib/clerk/models/components/signintoken.rb +4 -4
  135. data/lib/clerk/models/components/signup.rb +5 -5
  136. data/lib/clerk/models/components/signup_external_account.rb +1 -1
  137. data/lib/clerk/models/components/signupverification.rb +2 -2
  138. data/lib/clerk/models/components/template.rb +4 -4
  139. data/lib/clerk/models/components/template_object.rb +1 -1
  140. data/lib/clerk/models/components/testingtoken.rb +2 -2
  141. data/lib/clerk/models/components/token.rb +3 -3
  142. data/lib/clerk/models/components/token_object.rb +1 -1
  143. data/lib/clerk/models/components/totalcount.rb +2 -2
  144. data/lib/clerk/models/components/totalcount_object.rb +1 -1
  145. data/lib/clerk/models/components/user.rb +18 -18
  146. data/lib/clerk/models/components/user_object.rb +1 -1
  147. data/lib/clerk/models/components/verification_passkey_passkey.rb +4 -4
  148. data/lib/clerk/models/components/waitlistentry.rb +5 -5
  149. data/lib/clerk/models/components/waitlistentry_invitation.rb +5 -5
  150. data/lib/clerk/models/components/web3signature.rb +3 -3
  151. data/lib/clerk/models/components/web3wallet.rb +4 -4
  152. data/lib/clerk/models/components/web3wallet_admin.rb +3 -3
  153. data/lib/clerk/models/components/web3wallet_object.rb +1 -1
  154. data/lib/clerk/models/operations/actor.rb +1 -1
  155. data/lib/clerk/models/operations/clerkidpoauthaccesstoken.rb +1 -1
  156. data/lib/clerk/models/operations/createapikey_responsebody.rb +1 -1
  157. data/lib/clerk/models/operations/createbulkinvitations_requestbody.rb +1 -1
  158. data/lib/clerk/models/operations/createinvitation_request.rb +1 -1
  159. data/lib/clerk/models/operations/createm2mtoken_responsebody.rb +1 -1
  160. data/lib/clerk/models/operations/createoauthapplication_request.rb +2 -2
  161. data/lib/clerk/models/operations/createroleset_request.rb +1 -1
  162. data/lib/clerk/models/operations/createsamlconnection_requestbody_1.rb +3 -3
  163. data/lib/clerk/models/operations/createsamlconnection_requestbody_2.rb +3 -3
  164. data/lib/clerk/models/operations/createsessiontoken_responsebody.rb +1 -1
  165. data/lib/clerk/models/operations/createsessiontokenfromtemplate_responsebody.rb +1 -1
  166. data/lib/clerk/models/operations/createuser_request.rb +5 -5
  167. data/lib/clerk/models/operations/data.rb +1 -1
  168. data/lib/clerk/models/operations/deleteapikey_responsebody.rb +1 -1
  169. data/lib/clerk/models/operations/getapikey_responsebody.rb +1 -1
  170. data/lib/clerk/models/operations/getpublicinterstitial_request.rb +2 -2
  171. data/lib/clerk/models/operations/getuserlist_request.rb +2 -2
  172. data/lib/clerk/models/operations/getuserscount_request.rb +2 -2
  173. data/lib/clerk/models/operations/listallorganizationdomains_request.rb +8 -4
  174. data/lib/clerk/models/operations/listallorganizationdomains_request.rbi +2 -0
  175. data/lib/clerk/models/operations/listwaitlistentries_request.rb +1 -1
  176. data/lib/clerk/models/operations/m2m_token.rb +1 -1
  177. data/lib/clerk/models/operations/refreshsession_requestbody.rb +1 -1
  178. data/lib/clerk/models/operations/revokeapikey_responsebody.rb +1 -1
  179. data/lib/clerk/models/operations/revokem2mtoken_responsebody.rb +1 -1
  180. data/lib/clerk/models/operations/updateapikey_responsebody.rb +1 -1
  181. data/lib/clerk/models/operations/updateinstance_request.rb +2 -2
  182. data/lib/clerk/models/operations/updateoauthapplication_requestbody.rb +2 -2
  183. data/lib/clerk/models/operations/updateroleset_requestbody.rb +1 -1
  184. data/lib/clerk/models/operations/updatesamlconnection_requestbody.rb +2 -2
  185. data/lib/clerk/models/operations/updateuser_requestbody.rb +2 -2
  186. data/lib/clerk/models/operations/updateusermetadata_requestbody.rb +1 -1
  187. data/lib/clerk/models/operations/verifyapikey_responsebody.rb +1 -1
  188. data/lib/clerk/models/operations/verifym2mtoken_responsebody.rb +1 -1
  189. data/lib/clerk/models/operations/verifyoauthaccesstoken_request.rb +2 -2
  190. data/lib/clerk/models/operations/verifytotp_responsebody.rb +1 -1
  191. data/lib/clerk/oauthaccesstokens.rb +1 -1
  192. data/lib/clerk/oauthapplications.rb +2 -2
  193. data/lib/clerk/organizationdomains.rb +4 -4
  194. data/lib/clerk/organizationinvitations.rb +11 -11
  195. data/lib/clerk/organizationmemberships.rb +4 -4
  196. data/lib/clerk/organizationpermissions.rb +2 -2
  197. data/lib/clerk/organizationroles.rb +2 -2
  198. data/lib/clerk/organizations.rb +5 -5
  199. data/lib/clerk/phonenumbers.rb +2 -2
  200. data/lib/clerk/proxy.rb +1 -1
  201. data/lib/clerk/proxychecks.rb +3 -3
  202. data/lib/clerk/redirecturls.rb +1 -1
  203. data/lib/clerk/rolesets.rb +5 -5
  204. data/lib/clerk/samlconnections.rb +2 -2
  205. data/lib/clerk/sdkconfiguration.rb +3 -3
  206. data/lib/clerk/sessions.rb +5 -5
  207. data/lib/clerk/signintokens.rb +1 -1
  208. data/lib/clerk/signups.rb +1 -1
  209. data/lib/clerk/templates.rb +3 -3
  210. data/lib/clerk/users.rb +17 -17
  211. data/lib/clerk/utils/request_bodies.rb +8 -3
  212. data/lib/clerk/utils/security.rb +10 -0
  213. data/lib/clerk/utils/url.rb +11 -5
  214. data/lib/clerk/utils/utils.rb +33 -3
  215. data/lib/clerk/waitlistentries.rb +5 -5
  216. data/lib/crystalline/metadata_fields.rb +69 -10
  217. data/lib/crystalline/module.rb +9 -5
  218. data/lib/crystalline/types.rb +52 -0
  219. metadata +2 -2
@@ -173,7 +173,7 @@ module Clerk
173
173
  req_content_type, data, form = Utils.serialize_request_body(request, false, true, :request, :json)
174
174
  headers['content-type'] = req_content_type
175
175
 
176
- if form
176
+ if form && !form.empty?
177
177
  body = Utils.encode_form(form)
178
178
  elsif Utils.match_content_type(req_content_type, 'application/x-www-form-urlencoded')
179
179
  body = URI.encode_www_form(data)
@@ -311,10 +311,10 @@ module Clerk
311
311
  # Creates multiple waitlist entries for the provided email addresses.
312
312
  # You can choose whether to send confirmation emails by setting the `notify` parameter to `true` or `false` for each entry.
313
313
  # If the `notify` parameter is omitted, it defaults to `true`.
314
- #
314
+ #
315
315
  # If an email address is already on the waitlist, no new entry will be created and the existing waitlist entry will be returned.
316
316
  # Duplicate email addresses within the same request are not allowed.
317
- #
317
+ #
318
318
  # This endpoint is limited to a maximum of 50 entries per API call. If you need to add more entries, please make multiple requests.
319
319
  url, params = @sdk_configuration.get_server_details
320
320
  base_url = Utils.template_url(url, params)
@@ -324,7 +324,7 @@ module Clerk
324
324
  req_content_type, data, form = Utils.serialize_request_body(request, false, true, :request, :json)
325
325
  headers['content-type'] = req_content_type
326
326
 
327
- if form
327
+ if form && !form.empty?
328
328
  body = Utils.encode_form(form)
329
329
  elsif Utils.match_content_type(req_content_type, 'application/x-www-form-urlencoded')
330
330
  body = URI.encode_www_form(data)
@@ -618,7 +618,7 @@ module Clerk
618
618
  req_content_type, data, form = Utils.serialize_request_body(request, false, false, :body, :json)
619
619
  headers['content-type'] = req_content_type
620
620
 
621
- if form
621
+ if form && !form.empty?
622
622
  body = Utils.encode_form(form)
623
623
  elsif Utils.match_content_type(req_content_type, 'application/x-www-form-urlencoded')
624
624
  body = URI.encode_www_form(data)
@@ -37,19 +37,30 @@ module Crystalline
37
37
  def unmarshal_single(field_type, value, format_metadata = nil)
38
38
  decoder = format_metadata.fetch(:decoder, nil)
39
39
 
40
- if field_type.instance_of?(Class) && field_type.include?(::Crystalline::MetadataFields)
40
+ # Delegate complex Crystalline types to unmarshal_json
41
+ if field_type.is_a?(Crystalline::DiscriminatedUnion) ||
42
+ Crystalline::Utils.arr?(field_type) ||
43
+ Crystalline::Utils.hash?(field_type) ||
44
+ Crystalline::Utils.union?(field_type)
45
+ return Crystalline.unmarshal_json(value, field_type)
46
+ elsif field_type.instance_of?(Class) && field_type.include?(::Crystalline::MetadataFields)
41
47
  return field_type.from_dict(value)
42
48
  elsif field_type.to_s == 'Date'
43
49
  return Date.parse(value)
44
50
  elsif field_type.to_s == 'DateTime'
45
51
  return DateTime.parse(value)
46
52
  elsif field_type.to_s == 'Object'
47
- # rubocop:disable Lint/SuppressedException
48
- begin
49
- value = JSON.parse(value)
50
- rescue TypeError, JSON::ParserError
53
+ if value.is_a?(::String)
54
+ trimmed = value.lstrip
55
+ if trimmed.start_with?('{') || trimmed.start_with?('[')
56
+ # rubocop:disable Lint/SuppressedException
57
+ begin
58
+ value = JSON.parse(value)
59
+ rescue TypeError, JSON::ParserError
60
+ end
61
+ # rubocop:enable Lint/SuppressedException
62
+ end
51
63
  end
52
- # rubocop:enable Lint/SuppressedException
53
64
  return value
54
65
  elsif field_type.to_s == 'Float'
55
66
  return value.to_f
@@ -65,12 +76,27 @@ module Crystalline
65
76
  def from_dict(d)
66
77
  to_build = {}
67
78
 
79
+ # Collect lookup keys for regular fields so we can identify additional properties later
80
+ known_keys = {}
81
+ additional_props_fields = []
68
82
  fields.each do |field|
83
+ format_metadata = field.metadata.fetch(:format_json, {})
84
+ if format_metadata.fetch(:additional_properties, false)
85
+ additional_props_fields << field
86
+ else
87
+ lookup = format_metadata.fetch(:letter_case, nil).call
88
+ known_keys[lookup] = true
89
+ end
90
+ end
91
+
92
+ # Process regular fields
93
+ (fields - additional_props_fields).each do |field|
69
94
  key = field.name
70
95
  format_metadata = field.metadata.fetch(:format_json, {})
96
+ field_type = field.type
97
+
71
98
  lookup = format_metadata.fetch(:letter_case, nil).call
72
99
  value = d[lookup]
73
- field_type = field.type
74
100
  if ::Crystalline::Utils.nilable? field_type
75
101
  if value == 'null'
76
102
  to_build[key] = nil
@@ -78,13 +104,15 @@ module Crystalline
78
104
  end
79
105
  field_type = ::Crystalline::Utils.nilable_of(field_type)
80
106
  end
81
-
107
+
82
108
  # If field is not nilable, and the value is not in the dict, raise a KeyError
83
109
  raise KeyError, "key #{lookup} not found in hash" if value.nil? && !::Crystalline::Utils.nilable?(field.type)
84
110
  # If field is nilable, and the value is not in the dict, just move to the next field
85
111
  next if value.nil?
86
112
 
87
- if Crystalline::Utils.arr? field_type
113
+ if field_type.is_a?(Crystalline::DiscriminatedUnion)
114
+ to_build[key] = field_type.parse(value)
115
+ elsif Crystalline::Utils.arr? field_type
88
116
  inner_type = Crystalline::Utils.arr_of(field_type)
89
117
  unmarshalled_array = value.map { |f| unmarshal_single(inner_type, f, format_metadata) }
90
118
  to_build[key] = unmarshalled_array
@@ -135,6 +163,32 @@ module Crystalline
135
163
  to_build[key] = unmarshal_single(field_type, value, format_metadata)
136
164
  end
137
165
  end
166
+
167
+ # Process additional properties fields: collect remaining keys from the dict
168
+ additional_props_fields.each do |field|
169
+ key = field.name
170
+ format_metadata = field.metadata.fetch(:format_json, {})
171
+ field_type = field.type
172
+
173
+ remaining = d.reject { |k, _| known_keys.key?(k) }
174
+ if remaining.empty?
175
+ next if ::Crystalline::Utils.nilable?(field.type)
176
+
177
+ raise KeyError, 'no additional properties found in hash'
178
+ end
179
+
180
+ inner_field_type = field_type
181
+ inner_field_type = ::Crystalline::Utils.nilable_of(inner_field_type) if ::Crystalline::Utils.nilable?(inner_field_type)
182
+ if Crystalline::Utils.hash?(inner_field_type)
183
+ val_type = Crystalline::Utils.hash_of(inner_field_type)
184
+ # rubocop:disable Style/HashTransformValues
185
+ to_build[key] = remaining.map { |k, v| [k, unmarshal_single(val_type, v, format_metadata)] }.to_h
186
+ # rubocop:enable Style/HashTransformValues
187
+ else
188
+ to_build[key] = remaining
189
+ end
190
+ end
191
+
138
192
  new(**to_build)
139
193
  end
140
194
  end
@@ -176,6 +230,8 @@ module Crystalline
176
230
  fields.sort_by(&:name).each do |field|
177
231
  format_json_meta = field.metadata[:format_json]
178
232
  required = !format_json_meta.nil? && format_json_meta.include?(:required)
233
+ is_additional_props = !format_json_meta.nil? && format_json_meta.fetch(:additional_properties, false)
234
+
179
235
  if !format_json_meta.nil? && format_json_meta.include?(:letter_case)
180
236
  key = format_json_meta[:letter_case].call(field.name)
181
237
  else
@@ -186,7 +242,10 @@ module Crystalline
186
242
  next if f.nil? && !required
187
243
  result[key] = nil if f.nil? && required
188
244
 
189
- if f.is_a? ::Array
245
+ # Flatten additional properties into the parent object
246
+ if is_additional_props && f.is_a?(::Hash)
247
+ f.each { |k, v| result[k] = marshal_single(v) }
248
+ elsif f.is_a? ::Array
190
249
  result[key] = f.map { |o| marshal_single(o) }
191
250
  elsif f.is_a? ::Hash
192
251
  result[key] = f.transform_values { |v| marshal_single(v) }
@@ -11,9 +11,9 @@ module Crystalline
11
11
  end
12
12
 
13
13
  def self.to_dict(complex)
14
- if complex.is_a? Array
14
+ if complex.is_a? ::Array
15
15
  complex.map { |v| Crystalline.to_dict(v) }
16
- elsif complex.is_a? Hash
16
+ elsif complex.is_a? ::Hash
17
17
  complex.transform_values { |v| Crystalline.to_dict(v) }
18
18
  elsif complex.respond_to?(:class) && complex.class.include?(::Crystalline::MetadataFields)
19
19
  complex.to_dict
@@ -34,7 +34,9 @@ module Crystalline
34
34
  if Crystalline::Utils.nilable? type
35
35
  type = Crystalline::Utils.nilable_of type
36
36
  end
37
- if type.instance_of?(Class) && type.include?(::Crystalline::MetadataFields)
37
+ if type.is_a?(Crystalline::DiscriminatedUnion)
38
+ type.parse(data)
39
+ elsif type.instance_of?(Class) && type.include?(::Crystalline::MetadataFields)
38
40
  type.from_dict(data)
39
41
  elsif Crystalline::Utils.union? type
40
42
  union_types = Crystalline::Utils.get_union_types(type)
@@ -64,7 +66,7 @@ module Crystalline
64
66
  if val.class.respond_to? :enums
65
67
  val.serialize
66
68
  elsif val.is_a? DateTime
67
- val.strftime('%Y-%m-%dT%H:%M:%S.%NZ')
69
+ val.strftime('%Y-%m-%dT%H:%M:%S.%NZ').sub(/(\.\d*[1-9])0+Z\z/, '\1Z').sub(/\.0+Z\z/, 'Z')
68
70
  elsif val.nil?
69
71
  nil
70
72
  elsif primitives
@@ -121,7 +123,9 @@ module Crystalline
121
123
  inexact = 0
122
124
  unmatched = 0
123
125
 
124
- if value.class.include?(::Crystalline::MetadataFields)
126
+ if value.is_a?(Crystalline::Unknown)
127
+ return [0, 0, 0]
128
+ elsif value.class.include?(::Crystalline::MetadataFields)
125
129
  value.fields.each do |field|
126
130
  format_metadata = field.metadata.fetch(:format_json, {})
127
131
  lookup = format_metadata.fetch(:letter_case, nil)&.call
@@ -40,6 +40,58 @@ module Crystalline
40
40
  class Boolean
41
41
  end
42
42
 
43
+ # Wraps an unrecognized payload from an open discriminated union.
44
+ # Produced when the discriminator value is missing, unknown, or schema validation fails.
45
+ class Unknown
46
+ attr_reader :raw
47
+
48
+ def initialize(raw:)
49
+ @raw = raw
50
+ end
51
+
52
+ def unknown?
53
+ true
54
+ end
55
+ end
56
+
57
+ # Forward-compatible discriminated union parser.
58
+ # Known discriminator values are deserialized to their mapped types.
59
+ # Unknown or invalid payloads are captured as Crystalline::Unknown.
60
+ class DiscriminatedUnion
61
+ attr_reader :discriminator, :variants
62
+
63
+ def initialize(discriminator, variants)
64
+ @discriminator = discriminator
65
+ @variants = variants
66
+ end
67
+
68
+ def parse(payload)
69
+ unless payload.is_a?(::Hash)
70
+ return Unknown.new(raw: payload)
71
+ end
72
+
73
+ disc_value = payload[@discriminator]
74
+ unless disc_value.is_a?(::String)
75
+ return Unknown.new(raw: payload)
76
+ end
77
+
78
+ variant_type = @variants[disc_value]
79
+ unless variant_type
80
+ return Unknown.new(raw: payload)
81
+ end
82
+
83
+ begin
84
+ Crystalline.unmarshal_json(payload, variant_type)
85
+ rescue StandardError
86
+ Unknown.new(raw: payload)
87
+ end
88
+ end
89
+ end
90
+
91
+ def self.unknown?(value)
92
+ value.is_a?(Unknown)
93
+ end
94
+
43
95
  module Enum
44
96
  def self.included(base)
45
97
  base.extend(ClassMethods)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: clerk-sdk-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.1.0
4
+ version: 5.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Speakeasy
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-02-13 00:00:00.000000000 Z
11
+ date: 2026-02-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: base64