async-matrix 0.1.2 → 1.0.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 (341) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +131 -0
  3. data/data/matrix-spec/api/client-server/account-data.yaml +389 -0
  4. data/data/matrix-spec/api/client-server/account_deactivation.yaml +149 -0
  5. data/data/matrix-spec/api/client-server/admin.yaml +512 -0
  6. data/data/matrix-spec/api/client-server/administrative_contact.yaml +620 -0
  7. data/data/matrix-spec/api/client-server/appservice_ping.yaml +183 -0
  8. data/data/matrix-spec/api/client-server/appservice_room_directory.yaml +104 -0
  9. data/data/matrix-spec/api/client-server/authed-content-repo.yaml +590 -0
  10. data/data/matrix-spec/api/client-server/banning.yaml +177 -0
  11. data/data/matrix-spec/api/client-server/capabilities.yaml +235 -0
  12. data/data/matrix-spec/api/client-server/content-repo.yaml +887 -0
  13. data/data/matrix-spec/api/client-server/create_room.yaml +306 -0
  14. data/data/matrix-spec/api/client-server/cross_signing.yaml +300 -0
  15. data/data/matrix-spec/api/client-server/definitions/auth_data.yaml +34 -0
  16. data/data/matrix-spec/api/client-server/definitions/auth_response.yaml +62 -0
  17. data/data/matrix-spec/api/client-server/definitions/client_device.yaml +44 -0
  18. data/data/matrix-spec/api/client-server/definitions/client_event.yaml +49 -0
  19. data/data/matrix-spec/api/client-server/definitions/client_event_without_room_id.yaml +147 -0
  20. data/data/matrix-spec/api/client-server/definitions/cross_signing_key.yaml +57 -0
  21. data/data/matrix-spec/api/client-server/definitions/device_keys.yaml +70 -0
  22. data/data/matrix-spec/api/client-server/definitions/errors/error.yaml +26 -0
  23. data/data/matrix-spec/api/client-server/definitions/errors/rate_limited.yaml +34 -0
  24. data/data/matrix-spec/api/client-server/definitions/event_batch.yaml +22 -0
  25. data/data/matrix-spec/api/client-server/definitions/event_filter.yaml +55 -0
  26. data/data/matrix-spec/api/client-server/definitions/invite_3pid.yaml +45 -0
  27. data/data/matrix-spec/api/client-server/definitions/key_backup_auth_data.yaml +36 -0
  28. data/data/matrix-spec/api/client-server/definitions/key_backup_data.yaml +50 -0
  29. data/data/matrix-spec/api/client-server/definitions/key_backup_session_data.yaml +57 -0
  30. data/data/matrix-spec/api/client-server/definitions/m.login.terms_params.yaml +83 -0
  31. data/data/matrix-spec/api/client-server/definitions/m.mentions.yaml +33 -0
  32. data/data/matrix-spec/api/client-server/definitions/m.oauth_params.yaml +30 -0
  33. data/data/matrix-spec/api/client-server/definitions/m.relates_to.yaml +45 -0
  34. data/data/matrix-spec/api/client-server/definitions/megolm_export_session_data.yaml +39 -0
  35. data/data/matrix-spec/api/client-server/definitions/olm_payload.yaml +88 -0
  36. data/data/matrix-spec/api/client-server/definitions/one_time_keys.yaml +27 -0
  37. data/data/matrix-spec/api/client-server/definitions/openid_token.yaml +37 -0
  38. data/data/matrix-spec/api/client-server/definitions/protocol.yaml +44 -0
  39. data/data/matrix-spec/api/client-server/definitions/public_rooms_chunk.yaml +74 -0
  40. data/data/matrix-spec/api/client-server/definitions/public_rooms_response.yaml +59 -0
  41. data/data/matrix-spec/api/client-server/definitions/push_condition.yaml +53 -0
  42. data/data/matrix-spec/api/client-server/definitions/push_rule.yaml +53 -0
  43. data/data/matrix-spec/api/client-server/definitions/push_ruleset.yaml +206 -0
  44. data/data/matrix-spec/api/client-server/definitions/recent_emoji.yaml +29 -0
  45. data/data/matrix-spec/api/client-server/definitions/request_email_validation.yaml +36 -0
  46. data/data/matrix-spec/api/client-server/definitions/request_msisdn_validation.yaml +36 -0
  47. data/data/matrix-spec/api/client-server/definitions/request_token_response.yaml +46 -0
  48. data/data/matrix-spec/api/client-server/definitions/room_event_filter.yaml +60 -0
  49. data/data/matrix-spec/api/client-server/definitions/room_key_backup.yaml +37 -0
  50. data/data/matrix-spec/api/client-server/definitions/room_summary.yaml +44 -0
  51. data/data/matrix-spec/api/client-server/definitions/security.yaml +47 -0
  52. data/data/matrix-spec/api/client-server/definitions/sso_login_flow.yaml +98 -0
  53. data/data/matrix-spec/api/client-server/definitions/state_event_batch.yaml +21 -0
  54. data/data/matrix-spec/api/client-server/definitions/sync_filter.yaml +86 -0
  55. data/data/matrix-spec/api/client-server/definitions/tag.yaml +24 -0
  56. data/data/matrix-spec/api/client-server/definitions/third_party_signed.yaml +45 -0
  57. data/data/matrix-spec/api/client-server/definitions/timeline_batch.yaml +36 -0
  58. data/data/matrix-spec/api/client-server/definitions/user_identifier.yaml +27 -0
  59. data/data/matrix-spec/api/client-server/definitions/wellknown/full.yaml +38 -0
  60. data/data/matrix-spec/api/client-server/definitions/wellknown/homeserver.yaml +25 -0
  61. data/data/matrix-spec/api/client-server/definitions/wellknown/identity_server.yaml +25 -0
  62. data/data/matrix-spec/api/client-server/device_management.yaml +314 -0
  63. data/data/matrix-spec/api/client-server/directory.yaml +318 -0
  64. data/data/matrix-spec/api/client-server/event_context.yaml +161 -0
  65. data/data/matrix-spec/api/client-server/filter.yaml +224 -0
  66. data/data/matrix-spec/api/client-server/inviting.yaml +149 -0
  67. data/data/matrix-spec/api/client-server/joining.yaml +238 -0
  68. data/data/matrix-spec/api/client-server/key_backup.yaml +993 -0
  69. data/data/matrix-spec/api/client-server/keys.yaml +482 -0
  70. data/data/matrix-spec/api/client-server/kicking.yaml +110 -0
  71. data/data/matrix-spec/api/client-server/knocking.yaml +152 -0
  72. data/data/matrix-spec/api/client-server/leaving.yaml +163 -0
  73. data/data/matrix-spec/api/client-server/list_joined_rooms.yaml +68 -0
  74. data/data/matrix-spec/api/client-server/list_public_rooms.yaml +280 -0
  75. data/data/matrix-spec/api/client-server/login.yaml +321 -0
  76. data/data/matrix-spec/api/client-server/login_token.yaml +138 -0
  77. data/data/matrix-spec/api/client-server/logout.yaml +86 -0
  78. data/data/matrix-spec/api/client-server/message_pagination.yaml +194 -0
  79. data/data/matrix-spec/api/client-server/notifications.yaml +152 -0
  80. data/data/matrix-spec/api/client-server/oauth_server_metadata.yaml +238 -0
  81. data/data/matrix-spec/api/client-server/old_sync.yaml +379 -0
  82. data/data/matrix-spec/api/client-server/openid.yaml +98 -0
  83. data/data/matrix-spec/api/client-server/password_management.yaml +246 -0
  84. data/data/matrix-spec/api/client-server/peeking_events.yaml +122 -0
  85. data/data/matrix-spec/api/client-server/policy_server.yaml +82 -0
  86. data/data/matrix-spec/api/client-server/presence.yaml +169 -0
  87. data/data/matrix-spec/api/client-server/profile.yaml +366 -0
  88. data/data/matrix-spec/api/client-server/pusher.yaml +303 -0
  89. data/data/matrix-spec/api/client-server/pushrules.yaml +633 -0
  90. data/data/matrix-spec/api/client-server/read_markers.yaml +103 -0
  91. data/data/matrix-spec/api/client-server/receipts.yaml +139 -0
  92. data/data/matrix-spec/api/client-server/redaction.yaml +116 -0
  93. data/data/matrix-spec/api/client-server/refresh.yaml +119 -0
  94. data/data/matrix-spec/api/client-server/registration.yaml +488 -0
  95. data/data/matrix-spec/api/client-server/registration_tokens.yaml +93 -0
  96. data/data/matrix-spec/api/client-server/relations.yaml +382 -0
  97. data/data/matrix-spec/api/client-server/report_content.yaml +283 -0
  98. data/data/matrix-spec/api/client-server/room_event_by_timestamp.yaml +147 -0
  99. data/data/matrix-spec/api/client-server/room_initial_sync.yaml +188 -0
  100. data/data/matrix-spec/api/client-server/room_send.yaml +130 -0
  101. data/data/matrix-spec/api/client-server/room_state.yaml +159 -0
  102. data/data/matrix-spec/api/client-server/room_summary.yaml +138 -0
  103. data/data/matrix-spec/api/client-server/room_upgrades.yaml +130 -0
  104. data/data/matrix-spec/api/client-server/rooms.yaml +380 -0
  105. data/data/matrix-spec/api/client-server/search.yaml +385 -0
  106. data/data/matrix-spec/api/client-server/space_hierarchy.yaml +237 -0
  107. data/data/matrix-spec/api/client-server/sso_login_redirect.yaml +135 -0
  108. data/data/matrix-spec/api/client-server/support.yaml +142 -0
  109. data/data/matrix-spec/api/client-server/sync.yaml +692 -0
  110. data/data/matrix-spec/api/client-server/tags.yaml +183 -0
  111. data/data/matrix-spec/api/client-server/third_party_lookup.yaml +324 -0
  112. data/data/matrix-spec/api/client-server/third_party_membership.yaml +139 -0
  113. data/data/matrix-spec/api/client-server/threads_list.yaml +167 -0
  114. data/data/matrix-spec/api/client-server/to_device.yaml +104 -0
  115. data/data/matrix-spec/api/client-server/typing.yaml +103 -0
  116. data/data/matrix-spec/api/client-server/users.yaml +136 -0
  117. data/data/matrix-spec/api/client-server/versions.yaml +108 -0
  118. data/data/matrix-spec/api/client-server/voip.yaml +93 -0
  119. data/data/matrix-spec/api/client-server/wellknown.yaml +60 -0
  120. data/data/matrix-spec/api/client-server/whoami.yaml +121 -0
  121. data/data/matrix-spec/event-schemas/examples/core/event.json +6 -0
  122. data/data/matrix-spec/event-schemas/examples/core/room_edu.json +3 -0
  123. data/data/matrix-spec/event-schemas/examples/core/room_event.json +11 -0
  124. data/data/matrix-spec/event-schemas/examples/core/state_event.json +4 -0
  125. data/data/matrix-spec/event-schemas/examples/invite_room_state.json +18 -0
  126. data/data/matrix-spec/event-schemas/examples/knock_room_state.json +18 -0
  127. data/data/matrix-spec/event-schemas/examples/m.accepted_terms.yaml +10 -0
  128. data/data/matrix-spec/event-schemas/examples/m.call.answer.yaml +21 -0
  129. data/data/matrix-spec/event-schemas/examples/m.call.candidates.yaml +16 -0
  130. data/data/matrix-spec/event-schemas/examples/m.call.hangup.yaml +10 -0
  131. data/data/matrix-spec/event-schemas/examples/m.call.invite.yaml +22 -0
  132. data/data/matrix-spec/event-schemas/examples/m.call.negotiate.yaml +22 -0
  133. data/data/matrix-spec/event-schemas/examples/m.call.reject.yaml +9 -0
  134. data/data/matrix-spec/event-schemas/examples/m.call.sdp_stream_metadata_changed.yaml +16 -0
  135. data/data/matrix-spec/event-schemas/examples/m.call.select_answer.yaml +10 -0
  136. data/data/matrix-spec/event-schemas/examples/m.direct.yaml +10 -0
  137. data/data/matrix-spec/event-schemas/examples/m.dummy.yaml +4 -0
  138. data/data/matrix-spec/event-schemas/examples/m.forwarded_room_key.yaml +14 -0
  139. data/data/matrix-spec/event-schemas/examples/m.fully_read.yaml +7 -0
  140. data/data/matrix-spec/event-schemas/examples/m.identity_server.yaml +7 -0
  141. data/data/matrix-spec/event-schemas/examples/m.ignored_user_list.yaml +9 -0
  142. data/data/matrix-spec/event-schemas/examples/m.invite_permission_config.yaml +7 -0
  143. data/data/matrix-spec/event-schemas/examples/m.key.verification.accept.yaml +12 -0
  144. data/data/matrix-spec/event-schemas/examples/m.key.verification.cancel.yaml +8 -0
  145. data/data/matrix-spec/event-schemas/examples/m.key.verification.done.yaml +6 -0
  146. data/data/matrix-spec/event-schemas/examples/m.key.verification.key.yaml +7 -0
  147. data/data/matrix-spec/event-schemas/examples/m.key.verification.mac.yaml +10 -0
  148. data/data/matrix-spec/event-schemas/examples/m.key.verification.ready.yaml +10 -0
  149. data/data/matrix-spec/event-schemas/examples/m.key.verification.request.yaml +11 -0
  150. data/data/matrix-spec/event-schemas/examples/m.key.verification.start$m.sas.v1.yaml +12 -0
  151. data/data/matrix-spec/event-schemas/examples/m.key.verification.start.yaml +8 -0
  152. data/data/matrix-spec/event-schemas/examples/m.key_backup.yaml +7 -0
  153. data/data/matrix-spec/event-schemas/examples/m.marked_unread.yaml +7 -0
  154. data/data/matrix-spec/event-schemas/examples/m.policy.rule.room.yaml +10 -0
  155. data/data/matrix-spec/event-schemas/examples/m.policy.rule.server.yaml +10 -0
  156. data/data/matrix-spec/event-schemas/examples/m.policy.rule.user.yaml +10 -0
  157. data/data/matrix-spec/event-schemas/examples/m.presence.yaml +12 -0
  158. data/data/matrix-spec/event-schemas/examples/m.push_rules.yaml +177 -0
  159. data/data/matrix-spec/event-schemas/examples/m.reaction.yaml +11 -0
  160. data/data/matrix-spec/event-schemas/examples/m.receipt.yaml +18 -0
  161. data/data/matrix-spec/event-schemas/examples/m.recent_emoji.yaml +16 -0
  162. data/data/matrix-spec/event-schemas/examples/m.room.avatar.yaml +14 -0
  163. data/data/matrix-spec/event-schemas/examples/m.room.canonical_alias.yaml +12 -0
  164. data/data/matrix-spec/event-schemas/examples/m.room.create.yaml +13 -0
  165. data/data/matrix-spec/event-schemas/examples/m.room.encrypted$megolm.yaml +11 -0
  166. data/data/matrix-spec/event-schemas/examples/m.room.encrypted$olm.yaml +14 -0
  167. data/data/matrix-spec/event-schemas/examples/m.room.encryption.yaml +10 -0
  168. data/data/matrix-spec/event-schemas/examples/m.room.guest_access.yaml +8 -0
  169. data/data/matrix-spec/event-schemas/examples/m.room.history_visibility.yaml +8 -0
  170. data/data/matrix-spec/event-schemas/examples/m.room.join_rules$restricted.yaml +18 -0
  171. data/data/matrix-spec/event-schemas/examples/m.room.join_rules.yaml +8 -0
  172. data/data/matrix-spec/event-schemas/examples/m.room.member$invite_room_state.yaml +15 -0
  173. data/data/matrix-spec/event-schemas/examples/m.room.member$join_authorised_via_users_server.yaml +12 -0
  174. data/data/matrix-spec/event-schemas/examples/m.room.member$knock_room_state.yaml +15 -0
  175. data/data/matrix-spec/event-schemas/examples/m.room.member$third_party_invite.yaml +20 -0
  176. data/data/matrix-spec/event-schemas/examples/m.room.member.yaml +12 -0
  177. data/data/matrix-spec/event-schemas/examples/m.room.message$m.audio.yaml +14 -0
  178. data/data/matrix-spec/event-schemas/examples/m.room.message$m.emote.yaml +10 -0
  179. data/data/matrix-spec/event-schemas/examples/m.room.message$m.file.yaml +14 -0
  180. data/data/matrix-spec/event-schemas/examples/m.room.message$m.image.yaml +16 -0
  181. data/data/matrix-spec/event-schemas/examples/m.room.message$m.key.verification.request.yaml +19 -0
  182. data/data/matrix-spec/event-schemas/examples/m.room.message$m.location.yaml +18 -0
  183. data/data/matrix-spec/event-schemas/examples/m.room.message$m.notice.yaml +10 -0
  184. data/data/matrix-spec/event-schemas/examples/m.room.message$m.server_notice.yaml +11 -0
  185. data/data/matrix-spec/event-schemas/examples/m.room.message$m.text.yaml +10 -0
  186. data/data/matrix-spec/event-schemas/examples/m.room.message$m.video.yaml +23 -0
  187. data/data/matrix-spec/event-schemas/examples/m.room.name.yaml +8 -0
  188. data/data/matrix-spec/event-schemas/examples/m.room.pinned_events.yaml +8 -0
  189. data/data/matrix-spec/event-schemas/examples/m.room.policy.yaml +11 -0
  190. data/data/matrix-spec/event-schemas/examples/m.room.power_levels.yaml +24 -0
  191. data/data/matrix-spec/event-schemas/examples/m.room.redaction.yaml +8 -0
  192. data/data/matrix-spec/event-schemas/examples/m.room.server_acl.yaml +10 -0
  193. data/data/matrix-spec/event-schemas/examples/m.room.third_party_invite.yaml +14 -0
  194. data/data/matrix-spec/event-schemas/examples/m.room.tombstone.yaml +9 -0
  195. data/data/matrix-spec/event-schemas/examples/m.room.topic.yaml +16 -0
  196. data/data/matrix-spec/event-schemas/examples/m.room_key.withheld.yaml +12 -0
  197. data/data/matrix-spec/event-schemas/examples/m.room_key.yaml +10 -0
  198. data/data/matrix-spec/event-schemas/examples/m.room_key_request$cancel_request.yaml +8 -0
  199. data/data/matrix-spec/event-schemas/examples/m.room_key_request$request.yaml +14 -0
  200. data/data/matrix-spec/event-schemas/examples/m.secret.request.yaml +9 -0
  201. data/data/matrix-spec/event-schemas/examples/m.secret.send.yaml +7 -0
  202. data/data/matrix-spec/event-schemas/examples/m.space.child.yaml +10 -0
  203. data/data/matrix-spec/event-schemas/examples/m.space.parent.yaml +9 -0
  204. data/data/matrix-spec/event-schemas/examples/m.sticker.yaml +22 -0
  205. data/data/matrix-spec/event-schemas/examples/m.tag.yaml +9 -0
  206. data/data/matrix-spec/event-schemas/examples/m.typing.yaml +7 -0
  207. data/data/matrix-spec/event-schemas/moderation_policy_rule.yaml +32 -0
  208. data/data/matrix-spec/event-schemas/schema/components/m_text_content_block.yaml +28 -0
  209. data/data/matrix-spec/event-schemas/schema/components/sdp_stream_metadata.yaml +43 -0
  210. data/data/matrix-spec/event-schemas/schema/components/signed_third_party_invite.yaml +48 -0
  211. data/data/matrix-spec/event-schemas/schema/core-event-schema/call_event.yaml +27 -0
  212. data/data/matrix-spec/event-schemas/schema/core-event-schema/event.yaml +16 -0
  213. data/data/matrix-spec/event-schemas/schema/core-event-schema/msgtype_infos/avatar_info.yaml +30 -0
  214. data/data/matrix-spec/event-schemas/schema/core-event-schema/msgtype_infos/image_info.yaml +52 -0
  215. data/data/matrix-spec/event-schemas/schema/core-event-schema/msgtype_infos/thumbnail_info.yaml +22 -0
  216. data/data/matrix-spec/event-schemas/schema/core-event-schema/room_event.yaml +17 -0
  217. data/data/matrix-spec/event-schemas/schema/core-event-schema/state_event.yaml +8 -0
  218. data/data/matrix-spec/event-schemas/schema/core-event-schema/stripped_state.yaml +48 -0
  219. data/data/matrix-spec/event-schemas/schema/core-event-schema/sync_room_event.yaml +49 -0
  220. data/data/matrix-spec/event-schemas/schema/core-event-schema/sync_state_event.yaml +36 -0
  221. data/data/matrix-spec/event-schemas/schema/core-event-schema/unsigned_prop.yaml +23 -0
  222. data/data/matrix-spec/event-schemas/schema/m.accepted_terms.yaml +25 -0
  223. data/data/matrix-spec/event-schemas/schema/m.call.answer.yaml +44 -0
  224. data/data/matrix-spec/event-schemas/schema/m.call.candidates.yaml +52 -0
  225. data/data/matrix-spec/event-schemas/schema/m.call.hangup.yaml +57 -0
  226. data/data/matrix-spec/event-schemas/schema/m.call.invite.yaml +53 -0
  227. data/data/matrix-spec/event-schemas/schema/m.call.negotiate.yaml +78 -0
  228. data/data/matrix-spec/event-schemas/schema/m.call.reject.yaml +29 -0
  229. data/data/matrix-spec/event-schemas/schema/m.call.sdp_stream_metadata_changed.yaml +24 -0
  230. data/data/matrix-spec/event-schemas/schema/m.call.select_answer.yaml +29 -0
  231. data/data/matrix-spec/event-schemas/schema/m.direct.yaml +29 -0
  232. data/data/matrix-spec/event-schemas/schema/m.dummy.yaml +25 -0
  233. data/data/matrix-spec/event-schemas/schema/m.forwarded_room_key.yaml +70 -0
  234. data/data/matrix-spec/event-schemas/schema/m.fully_read.yaml +26 -0
  235. data/data/matrix-spec/event-schemas/schema/m.identity_server.yaml +28 -0
  236. data/data/matrix-spec/event-schemas/schema/m.ignored_user_list.yaml +30 -0
  237. data/data/matrix-spec/event-schemas/schema/m.invite_permission_config.yaml +21 -0
  238. data/data/matrix-spec/event-schemas/schema/m.key.verification.accept.yaml +63 -0
  239. data/data/matrix-spec/event-schemas/schema/m.key.verification.cancel.yaml +72 -0
  240. data/data/matrix-spec/event-schemas/schema/m.key.verification.done.yaml +24 -0
  241. data/data/matrix-spec/event-schemas/schema/m.key.verification.key.yaml +31 -0
  242. data/data/matrix-spec/event-schemas/schema/m.key.verification.m.relates_to.yaml +23 -0
  243. data/data/matrix-spec/event-schemas/schema/m.key.verification.mac.yaml +43 -0
  244. data/data/matrix-spec/event-schemas/schema/m.key.verification.ready.yaml +41 -0
  245. data/data/matrix-spec/event-schemas/schema/m.key.verification.request.yaml +47 -0
  246. data/data/matrix-spec/event-schemas/schema/m.key.verification.start$m.reciprocate.v1.yaml +45 -0
  247. data/data/matrix-spec/event-schemas/schema/m.key.verification.start$m.sas.v1.yaml +76 -0
  248. data/data/matrix-spec/event-schemas/schema/m.key.verification.start.yaml +46 -0
  249. data/data/matrix-spec/event-schemas/schema/m.key_backup.yaml +24 -0
  250. data/data/matrix-spec/event-schemas/schema/m.marked_unread.yaml +26 -0
  251. data/data/matrix-spec/event-schemas/schema/m.policy.rule.room.yaml +17 -0
  252. data/data/matrix-spec/event-schemas/schema/m.policy.rule.server.yaml +17 -0
  253. data/data/matrix-spec/event-schemas/schema/m.policy.rule.user.yaml +17 -0
  254. data/data/matrix-spec/event-schemas/schema/m.presence.yaml +50 -0
  255. data/data/matrix-spec/event-schemas/schema/m.push_rules.yaml +23 -0
  256. data/data/matrix-spec/event-schemas/schema/m.reaction.yaml +45 -0
  257. data/data/matrix-spec/event-schemas/schema/m.receipt.yaml +65 -0
  258. data/data/matrix-spec/event-schemas/schema/m.recent_emoji.yaml +29 -0
  259. data/data/matrix-spec/event-schemas/schema/m.room.avatar.yaml +29 -0
  260. data/data/matrix-spec/event-schemas/schema/m.room.canonical_alias.yaml +40 -0
  261. data/data/matrix-spec/event-schemas/schema/m.room.create.yaml +86 -0
  262. data/data/matrix-spec/event-schemas/schema/m.room.encrypted.yaml +91 -0
  263. data/data/matrix-spec/event-schemas/schema/m.room.encryption.yaml +38 -0
  264. data/data/matrix-spec/event-schemas/schema/m.room.guest_access.yaml +28 -0
  265. data/data/matrix-spec/event-schemas/schema/m.room.history_visibility.yaml +30 -0
  266. data/data/matrix-spec/event-schemas/schema/m.room.join_rules.yaml +78 -0
  267. data/data/matrix-spec/event-schemas/schema/m.room.member.yaml +174 -0
  268. data/data/matrix-spec/event-schemas/schema/m.room.message$m.audio.yaml +73 -0
  269. data/data/matrix-spec/event-schemas/schema/m.room.message$m.emote.yaml +36 -0
  270. data/data/matrix-spec/event-schemas/schema/m.room.message$m.file.yaml +85 -0
  271. data/data/matrix-spec/event-schemas/schema/m.room.message$m.image.yaml +63 -0
  272. data/data/matrix-spec/event-schemas/schema/m.room.message$m.key.verification.request.yaml +70 -0
  273. data/data/matrix-spec/event-schemas/schema/m.room.message$m.location.yaml +50 -0
  274. data/data/matrix-spec/event-schemas/schema/m.room.message$m.notice.yaml +36 -0
  275. data/data/matrix-spec/event-schemas/schema/m.room.message$m.server_notice.yaml +41 -0
  276. data/data/matrix-spec/event-schemas/schema/m.room.message$m.text.yaml +36 -0
  277. data/data/matrix-spec/event-schemas/schema/m.room.message$m.video.yaml +95 -0
  278. data/data/matrix-spec/event-schemas/schema/m.room.message.yaml +25 -0
  279. data/data/matrix-spec/event-schemas/schema/m.room.name.yaml +35 -0
  280. data/data/matrix-spec/event-schemas/schema/m.room.pinned_events.yaml +27 -0
  281. data/data/matrix-spec/event-schemas/schema/m.room.policy.yaml +41 -0
  282. data/data/matrix-spec/event-schemas/schema/m.room.power_levels.yaml +139 -0
  283. data/data/matrix-spec/event-schemas/schema/m.room.redaction.yaml +29 -0
  284. data/data/matrix-spec/event-schemas/schema/m.room.server_acl.yaml +88 -0
  285. data/data/matrix-spec/event-schemas/schema/m.room.third_party_invite.yaml +79 -0
  286. data/data/matrix-spec/event-schemas/schema/m.room.tombstone.yaml +29 -0
  287. data/data/matrix-spec/event-schemas/schema/m.room.topic.yaml +53 -0
  288. data/data/matrix-spec/event-schemas/schema/m.room_key.withheld.yaml +88 -0
  289. data/data/matrix-spec/event-schemas/schema/m.room_key.yaml +38 -0
  290. data/data/matrix-spec/event-schemas/schema/m.room_key_request.yaml +73 -0
  291. data/data/matrix-spec/event-schemas/schema/m.secret.request.yaml +42 -0
  292. data/data/matrix-spec/event-schemas/schema/m.secret.send.yaml +35 -0
  293. data/data/matrix-spec/event-schemas/schema/m.space.child.yaml +50 -0
  294. data/data/matrix-spec/event-schemas/schema/m.space.parent.yaml +36 -0
  295. data/data/matrix-spec/event-schemas/schema/m.sticker.yaml +36 -0
  296. data/data/matrix-spec/event-schemas/schema/m.tag.yaml +28 -0
  297. data/data/matrix-spec/event-schemas/schema/m.typing.yaml +29 -0
  298. data/lib/async/matrix/api/chain.rb +575 -0
  299. data/lib/async/matrix/api/concat.rb +105 -0
  300. data/lib/async/matrix/api/path_tree.rb +208 -0
  301. data/lib/async/matrix/api.rb +204 -0
  302. data/lib/async/matrix/application_service/bot.rb +235 -0
  303. data/lib/async/matrix/application_service/config/schema/analytics.json +21 -0
  304. data/lib/async/matrix/application_service/config/schema/appservice.json +82 -0
  305. data/lib/async/matrix/application_service/config/schema/backfill.json +91 -0
  306. data/lib/async/matrix/application_service/config/schema/bridge.json +209 -0
  307. data/lib/async/matrix/application_service/config/schema/config.json +61 -0
  308. data/lib/async/matrix/application_service/config/schema/database.json +38 -0
  309. data/lib/async/matrix/application_service/config/schema/direct_media.json +35 -0
  310. data/lib/async/matrix/application_service/config/schema/double_puppet.json +24 -0
  311. data/lib/async/matrix/application_service/config/schema/encryption.json +164 -0
  312. data/lib/async/matrix/application_service/config/schema/homeserver.json +58 -0
  313. data/lib/async/matrix/application_service/config/schema/logging.json +50 -0
  314. data/lib/async/matrix/application_service/config/schema/management_room_texts.json +25 -0
  315. data/lib/async/matrix/application_service/config/schema/matrix.json +45 -0
  316. data/lib/async/matrix/application_service/config/schema/permissions.json +54 -0
  317. data/lib/async/matrix/application_service/config/schema/provisioning.json +23 -0
  318. data/lib/async/matrix/application_service/config/schema/public_media.json +39 -0
  319. data/lib/async/matrix/application_service/config/schema/relay.json +43 -0
  320. data/lib/async/matrix/application_service/config/vivify.rb +110 -0
  321. data/lib/async/matrix/application_service/config.rb +214 -123
  322. data/lib/async/matrix/application_service/dispatcher.rb +115 -113
  323. data/lib/async/matrix/application_service/error_response.rb +26 -26
  324. data/lib/async/matrix/application_service/event.rb +217 -1
  325. data/lib/async/matrix/application_service/server.rb +286 -215
  326. data/lib/async/matrix/application_service/transaction.rb +52 -52
  327. data/lib/async/matrix/application_service/transaction_store.rb +62 -62
  328. data/lib/async/matrix/client.rb +919 -179
  329. data/lib/async/matrix/connection.rb +8 -8
  330. data/lib/async/matrix/endpoint.rb +45 -45
  331. data/lib/async/matrix/error.rb +49 -43
  332. data/lib/async/matrix/media_client.rb +173 -0
  333. data/lib/async/matrix/notifier.rb +92 -92
  334. data/lib/async/matrix/schema/registry.rb +355 -0
  335. data/lib/async/matrix/schema/validation_error.rb +226 -0
  336. data/lib/async/matrix/schema.rb +174 -0
  337. data/lib/async/matrix/server.rb +7 -7
  338. data/lib/async/matrix/stream.rb +7 -7
  339. data/lib/async/matrix/version.rb +1 -1
  340. data/lib/async/matrix.rb +5 -2
  341. metadata +353 -1
@@ -0,0 +1,355 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the Apache License, Version 2.0.
4
+ # Copyright, 2026, by General Intelligence Systems.
5
+
6
+ require "yaml"
7
+ require "json"
8
+ require "json_schemer"
9
+ require "pathname"
10
+
11
+ module Async
12
+ module Matrix
13
+ module Schema
14
+ # Loads and indexes Matrix event schemas from the official matrix-org/matrix-spec
15
+ # YAML files bundled in data/matrix-spec/event-schemas/schema/.
16
+ #
17
+ # Schemas are loaded lazily on first access and cached for the lifetime of the
18
+ # process. Each schema is a JSONSchemer::Schema instance that resolves relative
19
+ # $ref paths (e.g. core-event-schema/room_event.yaml) via a file-based resolver.
20
+ #
21
+ # registry = Async::Matrix::Schema::Registry.instance
22
+ # registry["m.room.message"] # => JSONSchemer::Schema
23
+ # registry.variant("m.room.message", "m.text") # => JSONSchemer::Schema
24
+ # registry.event_types # => ["m.accepted_terms", ...]
25
+ #
26
+ class Registry
27
+ # Custom format validators for Matrix-specific format hints.
28
+ MATRIX_FORMATS = {
29
+ "mx-user-id" => ->(value, _schema) { value.is_a?(String) && value.match?(/\A@[^:]+:.+\z/) },
30
+ "mx-room-id" => ->(value, _schema) { value.is_a?(String) && value.match?(/\A![^:]+:.+\z/) },
31
+ "mx-event-id" => ->(value, _schema) { value.is_a?(String) && value.match?(/\A\$/) },
32
+ "mx-room-alias" => ->(value, _schema) { value.is_a?(String) && value.match?(/\A#[^:]+:.+\z/) },
33
+ "mx-server-name" => ->(value, _schema) { value.is_a?(String) && !value.empty? },
34
+ "mx-unpadded-base64" => ->(value, _schema) { value.is_a?(String) && value.match?(/\A[A-Za-z0-9+\/]*\z/) },
35
+ }.freeze
36
+
37
+ GEM_ROOT = File.expand_path("../../../..", __dir__)
38
+ SCHEMA_DIR = File.join(GEM_ROOT, "data", "matrix-spec", "event-schemas", "schema")
39
+
40
+ @instance = nil
41
+
42
+ class << self
43
+ # Returns the singleton Registry instance, creating it on first access.
44
+ def instance
45
+ @instance ||= new
46
+ end
47
+
48
+ # Reset the singleton (useful for testing).
49
+ def reset!
50
+ @instance = nil
51
+ end
52
+ end
53
+
54
+ def initialize
55
+ @schemas = {}
56
+ @variants = {}
57
+ @loaded = false
58
+ end
59
+
60
+ # Look up a schema by Matrix event type (e.g. "m.room.message").
61
+ # Returns a JSONSchemer::Schema or nil if no schema exists for that type.
62
+ def [](event_type)
63
+ load_schemas! unless @loaded
64
+ @schemas[event_type]
65
+ end
66
+
67
+ # Look up a variant schema (e.g. variant("m.room.message", "m.text")).
68
+ # Returns a JSONSchemer::Schema or nil.
69
+ def variant(event_type, subtype)
70
+ load_schemas! unless @loaded
71
+ @variants[[event_type, subtype]]
72
+ end
73
+
74
+ # All known base event types (excludes variant subtypes).
75
+ # @return [Array<String>] sorted event type strings
76
+ def event_types
77
+ load_schemas! unless @loaded
78
+ @schemas.keys.sort
79
+ end
80
+
81
+ # All known variant keys as [event_type, subtype] pairs.
82
+ # @return [Array<Array(String, String)>]
83
+ def variant_types
84
+ load_schemas! unless @loaded
85
+ @variants.keys.sort
86
+ end
87
+
88
+ # Total number of schemas loaded (base + variants).
89
+ def size
90
+ load_schemas! unless @loaded
91
+ @schemas.size + @variants.size
92
+ end
93
+
94
+ # Validate an event hash against its schema.
95
+ # Returns an array of error hashes (empty if valid).
96
+ # If no schema exists for the event type, returns empty (lenient).
97
+ def validate(event_hash)
98
+ event_type = event_hash["type"]
99
+ return [] unless event_type
100
+
101
+ base = self[event_type]
102
+ return [] unless base
103
+
104
+ errors = base.validate(event_hash).to_a
105
+
106
+ # Also validate against variant if applicable
107
+ variant_key = detect_variant_key(event_type, event_hash)
108
+ if variant_key
109
+ variant_schema = variant(event_type, variant_key)
110
+ if variant_schema
111
+ errors.concat(variant_schema.validate(event_hash).to_a)
112
+ end
113
+ end
114
+
115
+ errors
116
+ end
117
+
118
+ # Boolean validation.
119
+ def valid?(event_hash)
120
+ validate(event_hash).empty?
121
+ end
122
+
123
+ # Content properties defined by the schema for a given event type.
124
+ # Returns an array of property name strings, or empty array if unknown.
125
+ def content_properties(event_type)
126
+ schema = self[event_type]
127
+ return [] unless schema
128
+
129
+ extract_content_properties(schema.value)
130
+ end
131
+
132
+ private
133
+
134
+ # Ref resolver that loads YAML/JSON files from disk.
135
+ # json_schemer calls this with a URI for each $ref it encounters.
136
+ def ref_resolver
137
+ @ref_resolver ||= proc do |uri|
138
+ path = uri.path
139
+ if path && File.exist?(path)
140
+ content = File.read(path)
141
+ if path.end_with?(".yaml", ".yml")
142
+ YAML.safe_load(content, permitted_classes: [Symbol])
143
+ else
144
+ JSON.parse(content)
145
+ end
146
+ else
147
+ raise JSONSchemer::UnknownRef, uri.to_s
148
+ end
149
+ end
150
+ end
151
+
152
+ # Build a JSONSchemer::Schema from a parsed YAML hash, anchored at the
153
+ # given file path so relative $ref paths resolve correctly.
154
+ def build_schemer(parsed, file_path)
155
+ uri = URI("file://#{File.expand_path(file_path)}")
156
+ JSONSchemer.schema(
157
+ parsed,
158
+ base_uri: uri,
159
+ ref_resolver: ref_resolver,
160
+ insert_property_defaults: false,
161
+ formats: MATRIX_FORMATS
162
+ )
163
+ end
164
+
165
+ # Scan the schema directory and load all event schemas.
166
+ def load_schemas!
167
+ return if @loaded
168
+
169
+ Dir.glob(File.join(SCHEMA_DIR, "*.yaml")).each do |path|
170
+ load_schema_file(path)
171
+ end
172
+
173
+ # Also load JSON schema files (a few older schemas are JSON)
174
+ Dir.glob(File.join(SCHEMA_DIR, "*.json")).each do |path|
175
+ load_schema_file(path)
176
+ end
177
+
178
+ @loaded = true
179
+ end
180
+
181
+ def load_schema_file(path)
182
+ filename = File.basename(path, File.extname(path))
183
+ content = File.read(path)
184
+ parsed = path.end_with?(".json") ? JSON.parse(content) : YAML.safe_load(content, permitted_classes: [Symbol])
185
+
186
+ schemer = build_schemer(parsed, path)
187
+
188
+ # Detect event type from schema: properties.type.enum[0]
189
+ declared_type = parsed.dig("properties", "type", "enum")&.first
190
+
191
+ if filename.include?("$")
192
+ # Variant schema: "m.room.message$m.text" -> base="m.room.message", sub="m.text"
193
+ parts = filename.split("$", 2)
194
+ base_type = declared_type || parts[0]
195
+ subtype = parts[1]
196
+ @variants[[base_type, subtype]] = schemer
197
+ elsif declared_type
198
+ @schemas[declared_type] = schemer
199
+ else
200
+ # Schemas without a type enum (like core-event-schema/) are skipped
201
+ # from the index -- they're only used as $ref targets.
202
+ end
203
+ rescue => e
204
+ # Don't let a single broken schema prevent loading the rest
205
+ warn "async-matrix: failed to load schema #{path}: #{e.message}"
206
+ end
207
+
208
+ # Detect the variant subtype key from an event hash.
209
+ # For m.room.message, the variant is the msgtype.
210
+ # For m.room.encrypted, the variant is the algorithm.
211
+ # For m.key.verification.start, the variant is the method.
212
+ def detect_variant_key(event_type, event_hash)
213
+ content = event_hash["content"]
214
+ return nil unless content.is_a?(Hash)
215
+
216
+ case event_type
217
+ when "m.room.message"
218
+ content["msgtype"]
219
+ when "m.room.encrypted"
220
+ content["algorithm"]
221
+ when "m.key.verification.start"
222
+ content["method"]
223
+ end
224
+ end
225
+
226
+ # Extract content property names from a schema's parsed YAML.
227
+ def extract_content_properties(schema_hash)
228
+ props = schema_hash.dig("properties", "content", "properties")
229
+ return [] unless props.is_a?(Hash)
230
+ props.keys
231
+ end
232
+ end
233
+ end
234
+ end
235
+ end
236
+
237
+ test do
238
+ describe "Async::Matrix::Schema::Registry" do
239
+ it "loads schemas from disk" do
240
+ Async::Matrix::Schema::Registry.instance.size.should.be > 0
241
+ end
242
+
243
+ it "has known event types" do
244
+ types = Async::Matrix::Schema::Registry.instance.event_types
245
+ types.should.include "m.room.message"
246
+ types.should.include "m.room.member"
247
+ types.should.include "m.room.create"
248
+ types.should.include "m.reaction"
249
+ end
250
+
251
+ it "returns a JSONSchemer::Schema for a known type" do
252
+ schema = Async::Matrix::Schema::Registry.instance["m.room.message"]
253
+ schema.should.not.be.nil
254
+ schema.should.be.kind_of JSONSchemer::Schema
255
+ end
256
+
257
+ it "returns nil for an unknown type" do
258
+ Async::Matrix::Schema::Registry.instance["m.fake.event"].should.be.nil
259
+ end
260
+
261
+ it "has variant schemas" do
262
+ Async::Matrix::Schema::Registry.instance.variant_types.should.not.be.empty
263
+ end
264
+
265
+ it "resolves variant schemas" do
266
+ schema = Async::Matrix::Schema::Registry.instance.variant("m.room.message", "m.text")
267
+ schema.should.not.be.nil
268
+ schema.should.be.kind_of JSONSchemer::Schema
269
+ end
270
+
271
+ it "validates a correct m.room.message event" do
272
+ event = {
273
+ "type" => "m.room.message",
274
+ "content" => {"msgtype" => "m.text", "body" => "hello"},
275
+ "event_id" => "$abc123",
276
+ "sender" => "@alice:example.org",
277
+ "origin_server_ts" => 1234567890,
278
+ "room_id" => "!room:example.org"
279
+ }
280
+ Async::Matrix::Schema::Registry.instance.valid?(event).should == true
281
+ end
282
+
283
+ it "rejects m.room.message missing body" do
284
+ event = {
285
+ "type" => "m.room.message",
286
+ "content" => {"msgtype" => "m.text"},
287
+ "event_id" => "$abc123",
288
+ "sender" => "@alice:example.org",
289
+ "origin_server_ts" => 1234567890,
290
+ "room_id" => "!room:example.org"
291
+ }
292
+ Async::Matrix::Schema::Registry.instance.valid?(event).should == false
293
+ end
294
+
295
+ it "is lenient with unknown event types" do
296
+ event = {
297
+ "type" => "m.room.other",
298
+ "content" => {"msgtype" => "m.text", "body" => "hi"},
299
+ "event_id" => "$abc123",
300
+ "sender" => "@alice:example.org",
301
+ "origin_server_ts" => 1234567890,
302
+ "room_id" => "!room:example.org"
303
+ }
304
+ Async::Matrix::Schema::Registry.instance.valid?(event).should == true
305
+ end
306
+
307
+ it "validates m.room.member with correct membership" do
308
+ event = {
309
+ "type" => "m.room.member",
310
+ "content" => {"membership" => "join"},
311
+ "state_key" => "@alice:example.org",
312
+ "event_id" => "$abc123",
313
+ "sender" => "@alice:example.org",
314
+ "origin_server_ts" => 1234567890,
315
+ "room_id" => "!room:example.org"
316
+ }
317
+ Async::Matrix::Schema::Registry.instance.valid?(event).should == true
318
+ end
319
+
320
+ it "rejects m.room.member with invalid membership" do
321
+ event = {
322
+ "type" => "m.room.member",
323
+ "content" => {"membership" => "invalid_state"},
324
+ "state_key" => "@alice:example.org",
325
+ "event_id" => "$abc123",
326
+ "sender" => "@alice:example.org",
327
+ "origin_server_ts" => 1234567890,
328
+ "room_id" => "!room:example.org"
329
+ }
330
+ Async::Matrix::Schema::Registry.instance.valid?(event).should == false
331
+ end
332
+
333
+ it "returns content properties for known types" do
334
+ props = Async::Matrix::Schema::Registry.instance.content_properties("m.room.message")
335
+ props.should.include "msgtype"
336
+ props.should.include "body"
337
+ end
338
+
339
+ it "returns empty content properties for unknown types" do
340
+ Async::Matrix::Schema::Registry.instance.content_properties("m.fake.type").should == []
341
+ end
342
+
343
+ it "performs variant validation for m.room.message with msgtype" do
344
+ event = {
345
+ "type" => "m.room.message",
346
+ "content" => {"msgtype" => "m.text", "body" => "hello"},
347
+ "event_id" => "$abc123",
348
+ "sender" => "@alice:example.org",
349
+ "origin_server_ts" => 1234567890,
350
+ "room_id" => "!room:example.org"
351
+ }
352
+ Async::Matrix::Schema::Registry.instance.validate(event).should == []
353
+ end
354
+ end
355
+ end
@@ -0,0 +1,226 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the Apache License, Version 2.0.
4
+ # Copyright, 2026, by General Intelligence Systems.
5
+
6
+ require_relative "../error"
7
+
8
+ module Async
9
+ module Matrix
10
+ module Schema
11
+ # Raised by Event#valid! when data fails schema validation.
12
+ #
13
+ # Produces detailed, human-readable error messages that identify the exact
14
+ # key path and offending value.
15
+ #
16
+ # Schema validation failed for m.room.member event $abc123:
17
+ # - content.membership = "invalid" -- must be one of: ["invite", "join", "knock", "leave", "ban"]
18
+ # - sender is required but missing
19
+ #
20
+ class ValidationError < Async::Matrix::Error
21
+ attr_reader :errors, :event_type, :event_id
22
+
23
+ # @param errors [Array<Hash>] raw JSONSchemer error hashes
24
+ # @param event_type [String, nil] Matrix event type (e.g. "m.room.member")
25
+ # @param event_id [String, nil] Matrix event ID (e.g. "$abc123")
26
+ def initialize(errors, event_type: nil, event_id: nil)
27
+ @errors = errors
28
+ @event_type = event_type
29
+ @event_id = event_id
30
+ super("M_BAD_JSON", build_message)
31
+ end
32
+
33
+ private
34
+
35
+ def build_message
36
+ lines = [header_line]
37
+ @errors.each { |e| lines.concat(Array(format_error(e))) }
38
+ lines.join("\n")
39
+ end
40
+
41
+ def header_line
42
+ header = "Schema validation failed"
43
+ header += " for #{@event_type}" if @event_type
44
+ header += " event #{@event_id}" if @event_id
45
+ header + ":"
46
+ end
47
+
48
+ def format_error(error)
49
+ path = pointer_to_dot(error["data_pointer"])
50
+ type = error["type"]
51
+ data = error["data"]
52
+
53
+ case type
54
+ when "required"
55
+ format_required(error, path)
56
+ when "string", "integer", "number", "boolean", "array", "object", "null"
57
+ " - #{path} = #{truncate(data.inspect)} -- expected #{type}, got #{data.class}"
58
+ when "minimum"
59
+ " - #{path} = #{truncate(data.inspect)} -- must be >= #{error.dig("schema", "minimum")}"
60
+ when "maximum"
61
+ " - #{path} = #{truncate(data.inspect)} -- must be <= #{error.dig("schema", "maximum")}"
62
+ when "enum"
63
+ allowed = error.dig("schema", "enum")
64
+ " - #{path} = #{truncate(data.inspect)} -- must be one of: #{truncate(allowed.inspect)}"
65
+ when "pattern"
66
+ pattern = error.dig("schema", "pattern")
67
+ " - #{path} = #{truncate(data.inspect)} -- does not match pattern: #{pattern}"
68
+ when "format"
69
+ fmt = error.dig("schema", "format")
70
+ " - #{path} = #{truncate(data.inspect)} -- invalid #{fmt} format"
71
+ when "minLength"
72
+ " - #{path} = #{truncate(data.inspect)} -- length must be >= #{error.dig("schema", "minLength")}"
73
+ when "maxLength"
74
+ " - #{path} = #{truncate(data.inspect)} -- length must be <= #{error.dig("schema", "maxLength")}"
75
+ when "minItems"
76
+ " - #{path} -- array must have >= #{error.dig("schema", "minItems")} items"
77
+ when "maxItems"
78
+ " - #{path} -- array must have <= #{error.dig("schema", "maxItems")} items"
79
+ when "uniqueItems"
80
+ " - #{path} -- array items must be unique"
81
+ when "const"
82
+ expected = error.dig("schema", "const")
83
+ " - #{path} = #{truncate(data.inspect)} -- must be #{truncate(expected.inspect)}"
84
+ when "additionalProperties"
85
+ " - #{path}: #{error["error"] || "has additional properties that are not allowed"}"
86
+ else
87
+ msg = error["error"] || type
88
+ " - #{path}: #{msg}"
89
+ end
90
+ end
91
+
92
+ def format_required(error, path)
93
+ missing = error.dig("details", "missing_keys") || []
94
+ if missing.empty?
95
+ " - #{path}: #{error["error"] || "required"}"
96
+ else
97
+ missing.map { |key| " - #{join_path(path, key)} is required but missing" }
98
+ end
99
+ end
100
+
101
+ def pointer_to_dot(pointer)
102
+ return "root" if pointer.nil? || pointer.empty?
103
+ pointer.delete_prefix("/").gsub("/", ".")
104
+ end
105
+
106
+ def join_path(base, key)
107
+ base == "root" ? key.to_s : "#{base}.#{key}"
108
+ end
109
+
110
+ def truncate(str, max: 60)
111
+ str.length > max ? "#{str[0...max]}..." : str
112
+ end
113
+ end
114
+ end
115
+ end
116
+ end
117
+
118
+ test do
119
+ describe "Async::Matrix::Schema::ValidationError" do
120
+ def error_hash(overrides = {})
121
+ {
122
+ "data" => nil,
123
+ "data_pointer" => "",
124
+ "schema" => {},
125
+ "schema_pointer" => "",
126
+ "root_schema" => {},
127
+ "type" => "unknown",
128
+ "error" => "something went wrong"
129
+ }.merge(overrides)
130
+ end
131
+
132
+ it "includes event type in header" do
133
+ err = Async::Matrix::Schema::ValidationError.new(
134
+ [error_hash],
135
+ event_type: "m.room.message"
136
+ )
137
+ err.message.should.include "Schema validation failed for m.room.message"
138
+ end
139
+
140
+ it "includes event ID in header" do
141
+ err = Async::Matrix::Schema::ValidationError.new(
142
+ [error_hash],
143
+ event_type: "m.room.member",
144
+ event_id: "$abc123"
145
+ )
146
+ err.message.should.include "m.room.member event $abc123"
147
+ end
148
+
149
+ it "formats type mismatch errors" do
150
+ err = Async::Matrix::Schema::ValidationError.new([
151
+ error_hash(
152
+ "data_pointer" => "/content/body",
153
+ "type" => "string",
154
+ "data" => 42
155
+ )
156
+ ])
157
+ err.message.should.include "content.body = 42 -- expected string, got Integer"
158
+ end
159
+
160
+ it "formats required errors with missing keys" do
161
+ err = Async::Matrix::Schema::ValidationError.new([
162
+ error_hash(
163
+ "data_pointer" => "/content",
164
+ "type" => "required",
165
+ "details" => {"missing_keys" => ["msgtype", "body"]}
166
+ )
167
+ ])
168
+ err.message.should.include "content.msgtype is required but missing"
169
+ err.message.should.include "content.body is required but missing"
170
+ end
171
+
172
+ it "formats enum errors" do
173
+ err = Async::Matrix::Schema::ValidationError.new([
174
+ error_hash(
175
+ "data_pointer" => "/content/membership",
176
+ "type" => "enum",
177
+ "data" => "invalid",
178
+ "schema" => {"enum" => %w[invite join knock leave ban]}
179
+ )
180
+ ])
181
+ err.message.should.include 'content.membership = "invalid" -- must be one of:'
182
+ err.message.should.include "invite"
183
+ end
184
+
185
+ it "formats pattern errors" do
186
+ err = Async::Matrix::Schema::ValidationError.new([
187
+ error_hash(
188
+ "data_pointer" => "/state_key",
189
+ "type" => "pattern",
190
+ "data" => "bad",
191
+ "schema" => {"pattern" => "^@"}
192
+ )
193
+ ])
194
+ err.message.should.include 'state_key = "bad" -- does not match pattern: ^@'
195
+ end
196
+
197
+ it "formats format errors" do
198
+ err = Async::Matrix::Schema::ValidationError.new([
199
+ error_hash(
200
+ "data_pointer" => "/content/avatar_url",
201
+ "type" => "format",
202
+ "data" => "not-a-uri",
203
+ "schema" => {"format" => "uri"}
204
+ )
205
+ ])
206
+ err.message.should.include "invalid uri format"
207
+ end
208
+
209
+ it "truncates long values" do
210
+ err = Async::Matrix::Schema::ValidationError.new([
211
+ error_hash(
212
+ "data_pointer" => "/content/body",
213
+ "type" => "integer",
214
+ "data" => "a" * 200
215
+ )
216
+ ])
217
+ err.message.should.include "..."
218
+ end
219
+
220
+ it "exposes the raw errors array" do
221
+ raw = [error_hash]
222
+ err = Async::Matrix::Schema::ValidationError.new(raw)
223
+ err.errors.should.equal raw
224
+ end
225
+ end
226
+ end