tigrbl-atoms 0.4.5.dev4__tar.gz → 0.4.5.dev6__tar.gz

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 (161) hide show
  1. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/PKG-INFO +19 -8
  2. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/README.md +16 -5
  3. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/pyproject.toml +5 -3
  4. tigrbl_atoms-0.4.5.dev6/tigrbl_atoms/_opview_helpers.py +377 -0
  5. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/framing/__init__.py +12 -0
  6. tigrbl_atoms-0.4.5.dev6/tigrbl_atoms/atoms/framing/websocket_jsonrpc.py +59 -0
  7. tigrbl_atoms-0.4.5.dev6/tigrbl_atoms/atoms/framing/webtransport_records.py +64 -0
  8. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/response/error_to_transport.py +5 -2
  9. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/sys/_oltp_context.py +28 -97
  10. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/sys/handler_bulk_delete.py +1 -2
  11. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/sys/handler_persistence.py +1 -2
  12. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/sys/handler_publish.py +1 -1
  13. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/sys/handler_subscribe.py +1 -1
  14. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/transport/__init__.py +2 -1
  15. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/transport/asgi_channel.py +79 -26
  16. tigrbl_atoms-0.4.5.dev6/tigrbl_atoms/atoms/transport/sink_bind.py +65 -0
  17. tigrbl_atoms-0.4.5.dev6/tigrbl_atoms/atoms/transport/websocket.py +112 -0
  18. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/wire/build_in.py +8 -22
  19. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/wire/validate_in.py +60 -68
  20. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/runtime_channel.py +20 -1
  21. tigrbl_atoms-0.4.5.dev4/tigrbl_atoms/_opview_helpers.py +0 -190
  22. tigrbl_atoms-0.4.5.dev4/tigrbl_atoms/atoms/transport/sink_bind.py +0 -37
  23. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/LICENSE +0 -0
  24. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/NOTICE +0 -0
  25. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/__init__.py +0 -0
  26. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/_ctx.py +0 -0
  27. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/_request.py +0 -0
  28. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/algebra.py +0 -0
  29. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/__init__.py +0 -0
  30. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/_temp.py +0 -0
  31. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/batch/__init__.py +0 -0
  32. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/batch/_scheduler.py +0 -0
  33. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/batch/_types.py +0 -0
  34. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/batch/abort_group.py +0 -0
  35. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/batch/admit.py +0 -0
  36. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/batch/await_seal.py +0 -0
  37. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/batch/cleanup.py +0 -0
  38. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/batch/commit.py +0 -0
  39. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/batch/dedupe.py +0 -0
  40. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/batch/error_project.py +0 -0
  41. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/batch/execute.py +0 -0
  42. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/batch/precommit_validate.py +0 -0
  43. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/batch/prepare_execute.py +0 -0
  44. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/batch/reject_admission.py +0 -0
  45. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/batch/result_slots.py +0 -0
  46. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/batch/scheduler.py +0 -0
  47. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/batch/seal_check.py +0 -0
  48. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/batch/tx_begin.py +0 -0
  49. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/dep/__init__.py +0 -0
  50. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/dep/_param_resolver.py +0 -0
  51. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/dep/extra.py +0 -0
  52. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/dep/security.py +0 -0
  53. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/dispatch/__init__.py +0 -0
  54. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/dispatch/binding_match.py +0 -0
  55. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/dispatch/binding_parse.py +0 -0
  56. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/dispatch/input_normalize.py +0 -0
  57. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/dispatch/op_resolve.py +0 -0
  58. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/egress/__init__.py +0 -0
  59. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/egress/asgi_send.py +0 -0
  60. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/egress/envelope_apply.py +0 -0
  61. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/egress/headers_apply.py +0 -0
  62. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/egress/http_finalize.py +0 -0
  63. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/egress/out_dump.py +0 -0
  64. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/egress/result_normalize.py +0 -0
  65. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/egress/to_transport_response.py +0 -0
  66. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/emit/__init__.py +0 -0
  67. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/emit/paired_post.py +0 -0
  68. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/emit/paired_pre.py +0 -0
  69. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/emit/readtime_alias.py +0 -0
  70. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/err/__init__.py +0 -0
  71. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/err/classify.py +0 -0
  72. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/err/ctx_build.py +0 -0
  73. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/err/rollback.py +0 -0
  74. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/err/transport_shape.py +0 -0
  75. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/fanout/__init__.py +0 -0
  76. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/fanout/emit_many.py +0 -0
  77. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/fanout/shape.py +0 -0
  78. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/framing/app_frame.py +0 -0
  79. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/framing/codec.py +0 -0
  80. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/hot/__init__.py +0 -0
  81. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/hot/slots.py +0 -0
  82. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/ingress/__init__.py +0 -0
  83. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/ingress/ctx_init.py +0 -0
  84. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/ingress/input_prepare.py +0 -0
  85. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/ingress/transport_extract.py +0 -0
  86. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/intent/__init__.py +0 -0
  87. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/intent/build.py +0 -0
  88. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/intent/final_group_key.py +0 -0
  89. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/intent/prekey.py +0 -0
  90. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/out/__init__.py +0 -0
  91. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/out/masking.py +0 -0
  92. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/refresh/__init__.py +0 -0
  93. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/refresh/demand.py +0 -0
  94. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/resolve/__init__.py +0 -0
  95. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/resolve/assemble.py +0 -0
  96. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/resolve/paired_gen.py +0 -0
  97. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/response/__init__.py +0 -0
  98. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/response/headers_from_payload.py +0 -0
  99. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/response/negotiate.py +0 -0
  100. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/response/negotiation.py +0 -0
  101. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/response/render.py +0 -0
  102. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/response/renderer.py +0 -0
  103. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/response/template.py +0 -0
  104. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/response/templates.py +0 -0
  105. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/schema/__init__.py +0 -0
  106. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/schema/collect_in.py +0 -0
  107. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/schema/collect_out.py +0 -0
  108. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/storage/__init__.py +0 -0
  109. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/storage/to_stored.py +0 -0
  110. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/sys/__init__.py +0 -0
  111. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/sys/_db.py +0 -0
  112. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/sys/commit_tx.py +0 -0
  113. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/sys/handler_aggregate.py +0 -0
  114. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/sys/handler_append_chunk.py +0 -0
  115. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/sys/handler_bulk_create.py +0 -0
  116. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/sys/handler_bulk_merge.py +0 -0
  117. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/sys/handler_bulk_replace.py +0 -0
  118. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/sys/handler_bulk_update.py +0 -0
  119. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/sys/handler_checkpoint.py +0 -0
  120. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/sys/handler_clear.py +0 -0
  121. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/sys/handler_close_session.py +0 -0
  122. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/sys/handler_close_stream.py +0 -0
  123. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/sys/handler_count.py +0 -0
  124. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/sys/handler_create.py +0 -0
  125. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/sys/handler_custom.py +0 -0
  126. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/sys/handler_delete.py +0 -0
  127. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/sys/handler_download.py +0 -0
  128. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/sys/handler_exists.py +0 -0
  129. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/sys/handler_group_by.py +0 -0
  130. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/sys/handler_list.py +0 -0
  131. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/sys/handler_merge.py +0 -0
  132. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/sys/handler_noop.py +0 -0
  133. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/sys/handler_open_bidi_stream.py +0 -0
  134. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/sys/handler_open_unidi_stream.py +0 -0
  135. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/sys/handler_read.py +0 -0
  136. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/sys/handler_replace.py +0 -0
  137. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/sys/handler_send_datagram.py +0 -0
  138. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/sys/handler_tail.py +0 -0
  139. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/sys/handler_update.py +0 -0
  140. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/sys/handler_upload.py +0 -0
  141. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/sys/handler_well_known.py +0 -0
  142. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/sys/phase_db.py +0 -0
  143. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/sys/start_tx.py +0 -0
  144. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/transport/completion_fence.py +0 -0
  145. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/transport/unit_capture.py +0 -0
  146. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/transport/websocket_unary.py +0 -0
  147. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/wire/__init__.py +0 -0
  148. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/wire/build_out.py +0 -0
  149. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/atoms/wire/dump.py +0 -0
  150. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/client_session_coverage.py +0 -0
  151. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/events.py +0 -0
  152. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/packed_inputs.py +0 -0
  153. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/phases.py +0 -0
  154. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/protocol_runtime.py +0 -0
  155. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/protocol_scope.py +0 -0
  156. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/runtime_callbacks.py +0 -0
  157. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/runtime_loop_regions.py +0 -0
  158. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/runtime_resume.py +0 -0
  159. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/runtime_transactions.py +0 -0
  160. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/stages.py +0 -0
  161. {tigrbl_atoms-0.4.5.dev4 → tigrbl_atoms-0.4.5.dev6}/tigrbl_atoms/types.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tigrbl-atoms
3
- Version: 0.4.5.dev4
3
+ Version: 0.4.5.dev6
4
4
  Summary: Runtime atom package for Tigrbl stages, phases, typed contexts, event anchors, protocol execution, and composable pipeline algebra.
5
5
  License: Apache License
6
6
  Version 2.0, January 2004
@@ -232,7 +232,7 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
232
232
  Classifier: Typing :: Typed
233
233
  Requires-Dist: jinja2 (>=3.1)
234
234
  Requires-Dist: sqlalchemy (>=2.0)
235
- Requires-Dist: tigrbl-core
235
+ Requires-Dist: tigrbl-core (>=0.4.5.dev6)
236
236
  Requires-Dist: tigrbl-ops-olap
237
237
  Requires-Dist: tigrbl-ops-oltp
238
238
  Requires-Dist: tigrbl-ops-realtime
@@ -243,7 +243,7 @@ Project-URL: Discord, https://discord.gg/K4YTAPapjR
243
243
  Project-URL: Homepage, https://github.com/tigrbl/tigrbl
244
244
  Project-URL: Issues, https://github.com/tigrbl/tigrbl/issues
245
245
  Project-URL: Organization, https://github.com/tigrbl
246
- Project-URL: Repository, https://github.com/tigrbl/tigrbl/tree/master/pkgs/core/tigrbl_atoms
246
+ Project-URL: Repository, https://github.com/tigrbl/tigrbl/tree/master/pkgs/40_atoms/tigrbl_atoms
247
247
  Description-Content-Type: text/markdown
248
248
 
249
249
  <div align="center">
@@ -253,7 +253,7 @@ Description-Content-Type: text/markdown
253
253
  <a href="https://pypi.org/project/tigrbl-atoms/"><img src="https://img.shields.io/pypi/v/tigrbl-atoms?label=PyPI" alt="PyPI version for tigrbl-atoms"/></a>
254
254
  <a href="https://pypi.org/project/tigrbl-atoms/"><img src="https://static.pepy.tech/badge/tigrbl-atoms" alt="Downloads for tigrbl-atoms"/></a>
255
255
  <a href="https://discord.gg/K4YTAPapjR"><img src="https://img.shields.io/badge/Discord-Join%20chat-5865F2?logo=discord&logoColor=white" alt="Discord community for tigrbl-atoms"/></a>
256
- <a href="https://github.com/tigrbl/tigrbl/blob/master/pkgs/core/tigrbl_atoms/README.md"><img src="https://hits.sh/github.com/tigrbl/tigrbl/blob/master/pkgs/core/tigrbl_atoms/README.md.svg?label=hits" alt="Repository hits for tigrbl-atoms README"/></a>
256
+ <a href="https://github.com/tigrbl/tigrbl/blob/master/pkgs/40_atoms/tigrbl_atoms/README.md"><img src="https://hits.sh/github.com/tigrbl/tigrbl/blob/master/pkgs/40_atoms/tigrbl_atoms/README.md.svg?label=hits" alt="Repository hits for tigrbl-atoms README"/></a>
257
257
  <a href="LICENSE"><img src="https://img.shields.io/badge/license-Apache%202.0-525252" alt="Apache 2.0 license"/></a>
258
258
  <a href="pyproject.toml"><img src="https://img.shields.io/badge/python-3.10%2C%203.11%2C%203.12%2C%203.13%2C%203.14-3776ab" alt="Python versions 3.10 | 3.11 | 3.12 | 3.13 | 3.14 for tigrbl-atoms"/></a>
259
259
  <a href="https://github.com/tigrbl/tigrbl/blob/master/docs/README.md"><img src="https://img.shields.io/badge/workspace-core-1f6feb" alt="Workspace group for tigrbl-atoms"/></a>
@@ -277,12 +277,23 @@ Framework maintainers, extension authors, and advanced users composing Tigrbl fr
277
277
 
278
278
  ## Where does tigrbl-atoms fit?
279
279
 
280
- `tigrbl-atoms` lives at `pkgs/core/tigrbl_atoms` and serves a focused layer in the split Tigrbl framework.
280
+ `tigrbl-atoms` lives at `pkgs/40_atoms/tigrbl_atoms` and serves a focused layer in the split Tigrbl framework.
281
281
 
282
282
  ## How does tigrbl-atoms work?
283
283
 
284
284
  It owns a narrow layer in the split workspace and is consumed by higher-level packages through explicit dependencies.
285
285
 
286
+ Inbound materialization belongs to `wire.validate_in`, which uses the core schema
287
+ materializer and declared field types (or mapped storage types when no field type
288
+ is declared). Member identifiers are selector inputs even when the primary key
289
+ is not writable. Route selectors take precedence over payload identifiers and
290
+ invalid selectors raise 422 before persistence. Persistence atoms consume the
291
+ validated payload and path carriers, including bulk values; CRUD helpers do not
292
+ repeat type coercion. Selector operations currently use the full atom pipeline
293
+ because the compiled scalar input shortcut does not implement route alias and
294
+ selector precedence validation. CRUD operations retain their existing flush-only
295
+ transaction contract; the caller owns commit or rollback.
296
+
286
297
 
287
298
  ## Install
288
299
 
@@ -299,7 +310,7 @@ pip install tigrbl-atoms
299
310
  | Surface | Value |
300
311
  |---|---|
301
312
  | PyPI package | [`tigrbl-atoms`](https://pypi.org/project/tigrbl-atoms/) |
302
- | Repository path | [`pkgs/core/tigrbl_atoms`](https://github.com/tigrbl/tigrbl/tree/master/pkgs/core/tigrbl_atoms) |
313
+ | Repository path | [`pkgs/40_atoms/tigrbl_atoms`](https://github.com/tigrbl/tigrbl/tree/master/pkgs/40_atoms/tigrbl_atoms) |
303
314
  | Python import root | `tigrbl_atoms` |
304
315
  | Console scripts | none declared |
305
316
  | Entry points | none declared |
@@ -466,7 +477,7 @@ Choose `tigrbl-atoms` when the quick-answer table matches your use case. Choose
466
477
 
467
478
  - Community: [Discord](https://discord.gg/K4YTAPapjR).
468
479
  - Issues: [GitHub Issues](https://github.com/tigrbl/tigrbl/issues).
469
- - Repository: [pkgs/core/tigrbl_atoms](https://github.com/tigrbl/tigrbl/tree/master/pkgs/core/tigrbl_atoms).
480
+ - Repository: [pkgs/40_atoms/tigrbl_atoms](https://github.com/tigrbl/tigrbl/tree/master/pkgs/40_atoms/tigrbl_atoms).
470
481
 
471
482
  ## Package-local Boundary
472
483
 
@@ -477,7 +488,7 @@ This file is a package-local distribution entry point. This README is the packag
477
488
  - Package status: governed package in the `tigrbl/tigrbl` workspace.
478
489
  - Governance source: [SSOT registry](https://github.com/tigrbl/tigrbl/blob/master/.ssot/registry.json).
479
490
  - Release evidence: [publish workflow](https://github.com/tigrbl/tigrbl/actions/workflows/publish.yml) validates package builds, tests, GitHub release assets, and PyPI publication for managed packages.
480
- - Local certification guard: `pkgs/core/tigrbl_tests/tests/unit/test_package_badges_and_notices.py` verifies every package README keeps the Discord badge, Apache 2.0 badge, explicit Python-version badge, `LICENSE`, and `NOTICE`.
491
+ - Local certification guard: `pkgs/97_tests/tigrbl_tests/tests/unit/test_package_badges_and_notices.py` verifies every package README keeps the Discord badge, Apache 2.0 badge, explicit Python-version badge, `LICENSE`, and `NOTICE`.
481
492
  - Scope note: this README documents the package boundary. Runtime feature support remains governed by `.ssot/` entities and the conformance docs linked below.
482
493
 
483
494
  ## License
@@ -5,7 +5,7 @@
5
5
  <a href="https://pypi.org/project/tigrbl-atoms/"><img src="https://img.shields.io/pypi/v/tigrbl-atoms?label=PyPI" alt="PyPI version for tigrbl-atoms"/></a>
6
6
  <a href="https://pypi.org/project/tigrbl-atoms/"><img src="https://static.pepy.tech/badge/tigrbl-atoms" alt="Downloads for tigrbl-atoms"/></a>
7
7
  <a href="https://discord.gg/K4YTAPapjR"><img src="https://img.shields.io/badge/Discord-Join%20chat-5865F2?logo=discord&logoColor=white" alt="Discord community for tigrbl-atoms"/></a>
8
- <a href="https://github.com/tigrbl/tigrbl/blob/master/pkgs/core/tigrbl_atoms/README.md"><img src="https://hits.sh/github.com/tigrbl/tigrbl/blob/master/pkgs/core/tigrbl_atoms/README.md.svg?label=hits" alt="Repository hits for tigrbl-atoms README"/></a>
8
+ <a href="https://github.com/tigrbl/tigrbl/blob/master/pkgs/40_atoms/tigrbl_atoms/README.md"><img src="https://hits.sh/github.com/tigrbl/tigrbl/blob/master/pkgs/40_atoms/tigrbl_atoms/README.md.svg?label=hits" alt="Repository hits for tigrbl-atoms README"/></a>
9
9
  <a href="LICENSE"><img src="https://img.shields.io/badge/license-Apache%202.0-525252" alt="Apache 2.0 license"/></a>
10
10
  <a href="pyproject.toml"><img src="https://img.shields.io/badge/python-3.10%2C%203.11%2C%203.12%2C%203.13%2C%203.14-3776ab" alt="Python versions 3.10 | 3.11 | 3.12 | 3.13 | 3.14 for tigrbl-atoms"/></a>
11
11
  <a href="https://github.com/tigrbl/tigrbl/blob/master/docs/README.md"><img src="https://img.shields.io/badge/workspace-core-1f6feb" alt="Workspace group for tigrbl-atoms"/></a>
@@ -29,12 +29,23 @@ Framework maintainers, extension authors, and advanced users composing Tigrbl fr
29
29
 
30
30
  ## Where does tigrbl-atoms fit?
31
31
 
32
- `tigrbl-atoms` lives at `pkgs/core/tigrbl_atoms` and serves a focused layer in the split Tigrbl framework.
32
+ `tigrbl-atoms` lives at `pkgs/40_atoms/tigrbl_atoms` and serves a focused layer in the split Tigrbl framework.
33
33
 
34
34
  ## How does tigrbl-atoms work?
35
35
 
36
36
  It owns a narrow layer in the split workspace and is consumed by higher-level packages through explicit dependencies.
37
37
 
38
+ Inbound materialization belongs to `wire.validate_in`, which uses the core schema
39
+ materializer and declared field types (or mapped storage types when no field type
40
+ is declared). Member identifiers are selector inputs even when the primary key
41
+ is not writable. Route selectors take precedence over payload identifiers and
42
+ invalid selectors raise 422 before persistence. Persistence atoms consume the
43
+ validated payload and path carriers, including bulk values; CRUD helpers do not
44
+ repeat type coercion. Selector operations currently use the full atom pipeline
45
+ because the compiled scalar input shortcut does not implement route alias and
46
+ selector precedence validation. CRUD operations retain their existing flush-only
47
+ transaction contract; the caller owns commit or rollback.
48
+
38
49
 
39
50
  ## Install
40
51
 
@@ -51,7 +62,7 @@ pip install tigrbl-atoms
51
62
  | Surface | Value |
52
63
  |---|---|
53
64
  | PyPI package | [`tigrbl-atoms`](https://pypi.org/project/tigrbl-atoms/) |
54
- | Repository path | [`pkgs/core/tigrbl_atoms`](https://github.com/tigrbl/tigrbl/tree/master/pkgs/core/tigrbl_atoms) |
65
+ | Repository path | [`pkgs/40_atoms/tigrbl_atoms`](https://github.com/tigrbl/tigrbl/tree/master/pkgs/40_atoms/tigrbl_atoms) |
55
66
  | Python import root | `tigrbl_atoms` |
56
67
  | Console scripts | none declared |
57
68
  | Entry points | none declared |
@@ -218,7 +229,7 @@ Choose `tigrbl-atoms` when the quick-answer table matches your use case. Choose
218
229
 
219
230
  - Community: [Discord](https://discord.gg/K4YTAPapjR).
220
231
  - Issues: [GitHub Issues](https://github.com/tigrbl/tigrbl/issues).
221
- - Repository: [pkgs/core/tigrbl_atoms](https://github.com/tigrbl/tigrbl/tree/master/pkgs/core/tigrbl_atoms).
232
+ - Repository: [pkgs/40_atoms/tigrbl_atoms](https://github.com/tigrbl/tigrbl/tree/master/pkgs/40_atoms/tigrbl_atoms).
222
233
 
223
234
  ## Package-local Boundary
224
235
 
@@ -229,7 +240,7 @@ This file is a package-local distribution entry point. This README is the packag
229
240
  - Package status: governed package in the `tigrbl/tigrbl` workspace.
230
241
  - Governance source: [SSOT registry](https://github.com/tigrbl/tigrbl/blob/master/.ssot/registry.json).
231
242
  - Release evidence: [publish workflow](https://github.com/tigrbl/tigrbl/actions/workflows/publish.yml) validates package builds, tests, GitHub release assets, and PyPI publication for managed packages.
232
- - Local certification guard: `pkgs/core/tigrbl_tests/tests/unit/test_package_badges_and_notices.py` verifies every package README keeps the Discord badge, Apache 2.0 badge, explicit Python-version badge, `LICENSE`, and `NOTICE`.
243
+ - Local certification guard: `pkgs/97_tests/tigrbl_tests/tests/unit/test_package_badges_and_notices.py` verifies every package README keeps the Discord badge, Apache 2.0 badge, explicit Python-version badge, `LICENSE`, and `NOTICE`.
233
244
  - Scope note: this README documents the package boundary. Runtime feature support remains governed by `.ssot/` entities and the conformance docs linked below.
234
245
 
235
246
  ## License
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "tigrbl-atoms"
3
- version = "0.4.5.dev4"
3
+ version = "0.4.5.dev6"
4
4
  description = "Runtime atom package for Tigrbl stages, phases, typed contexts, event anchors, protocol execution, and composable pipeline algebra."
5
5
  readme = "README.md"
6
6
  license = { file = "LICENSE" }
@@ -55,7 +55,7 @@ dependencies = [
55
55
  "tigrbl-ops-olap",
56
56
  "tigrbl-ops-realtime",
57
57
  "tigrbl-ops-webtransport",
58
- "tigrbl-core",
58
+ "tigrbl-core>=0.4.5.dev6",
59
59
  "tigrbl-typing",
60
60
  "jinja2>=3.1",
61
61
  "sqlalchemy>=2.0",
@@ -67,10 +67,11 @@ dependencies = [
67
67
  Organization = "https://github.com/tigrbl"
68
68
  Discord = "https://discord.gg/K4YTAPapjR"
69
69
  Homepage = "https://github.com/tigrbl/tigrbl"
70
- Repository = "https://github.com/tigrbl/tigrbl/tree/master/pkgs/core/tigrbl_atoms"
70
+ Repository = "https://github.com/tigrbl/tigrbl/tree/master/pkgs/40_atoms/tigrbl_atoms"
71
71
  Issues = "https://github.com/tigrbl/tigrbl/issues"
72
72
 
73
73
  [tool.uv.sources]
74
+ "tigrbl-kernel" = { workspace = true }
74
75
  "tigrbl-ops-oltp" = { workspace = true }
75
76
  "tigrbl-ops-olap" = { workspace = true }
76
77
  "tigrbl-ops-realtime" = { workspace = true }
@@ -90,6 +91,7 @@ packages = [
90
91
 
91
92
  [dependency-groups]
92
93
  dev = [
94
+ "tigrbl-kernel",
93
95
  "pytest>=8.0",
94
96
  "pytest-asyncio>=0.24.0",
95
97
  "pytest-timeout>=2.3.1",
@@ -0,0 +1,377 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Mapping
4
+ from typing import Any
5
+
6
+ from tigrbl_core.schema.builder.helpers import (
7
+ _spec_python_type,
8
+ _input_selector_entries,
9
+ _input_filter_entries,
10
+ )
11
+
12
+ _READ_ONLY_TARGETS = frozenset(
13
+ {
14
+ "read",
15
+ "list",
16
+ "count",
17
+ "exists",
18
+ "aggregate",
19
+ "group_by",
20
+ "subscribe",
21
+ "tail",
22
+ "download",
23
+ }
24
+ )
25
+ _NON_PERSISTING_TARGETS = frozenset(
26
+ {"delete", "clear", "bulk_delete", "close_stream", "close_session"}
27
+ )
28
+ _CREATE_LIKE_TARGETS = frozenset({"create", "bulk_create"})
29
+ _UPDATE_LIKE_TARGETS = frozenset({"update", "merge", "bulk_update", "bulk_merge"})
30
+ _REPLACE_LIKE_TARGETS = frozenset({"replace", "bulk_replace"})
31
+
32
+
33
+ def _storage_requires_input(storage: Any, op: str) -> bool:
34
+ if storage is None or op == "update":
35
+ return False
36
+ if bool(getattr(storage, "primary_key", False)):
37
+ if op in {"replace", "delete"}:
38
+ return True
39
+ auto = getattr(storage, "autoincrement", False)
40
+ if auto not in (False, None) or getattr(storage, "identity", None) is not None:
41
+ return False
42
+ has_default = (
43
+ getattr(storage, "default", None) is not None
44
+ or getattr(storage, "server_default", None) is not None
45
+ or callable(getattr(storage, "default_factory", None))
46
+ )
47
+ return not bool(getattr(storage, "nullable", True)) and not has_default
48
+
49
+
50
+ def _storage_has_server_default(spec: Any, storage: Any) -> bool:
51
+ return bool(
52
+ getattr(storage, "default", None) is not None
53
+ or getattr(storage, "server_default", None) is not None
54
+ or callable(getattr(storage, "default_factory", None))
55
+ or callable(getattr(spec, "default_factory", None))
56
+ )
57
+
58
+
59
+ def _supports_stored_shape(op: str) -> bool:
60
+ lowered = str(op or "").lower()
61
+ return lowered not in _READ_ONLY_TARGETS and lowered not in _NON_PERSISTING_TARGETS
62
+
63
+
64
+ def _is_input_enabled(io: Any, op: str) -> bool:
65
+ return op in set(getattr(io, "in_verbs", ()) or ())
66
+
67
+
68
+ def _is_paired_enabled(io: Any, op: str) -> bool:
69
+ cfg = getattr(io, "_paired", None)
70
+ if cfg is None:
71
+ return False
72
+ return op in set(getattr(cfg, "verbs", ()) or ())
73
+
74
+
75
+ def _include_in_stored_schema(spec: Any, io: Any, storage: Any, op: str) -> bool:
76
+ lowered = str(op or "").lower()
77
+ if storage is None or not _supports_stored_shape(lowered):
78
+ return False
79
+ if _is_input_enabled(io, op) or _is_paired_enabled(io, op):
80
+ return True
81
+ if lowered in _CREATE_LIKE_TARGETS or lowered in _UPDATE_LIKE_TARGETS:
82
+ return True
83
+ if lowered in _REPLACE_LIKE_TARGETS:
84
+ return True
85
+ return False
86
+
87
+
88
+ def _stored_required(spec: Any, storage: Any, op: str, *, derived: bool) -> bool:
89
+ lowered = str(op or "").lower()
90
+ if lowered in _UPDATE_LIKE_TARGETS:
91
+ return False
92
+ if lowered in _READ_ONLY_TARGETS or lowered in _NON_PERSISTING_TARGETS:
93
+ return False
94
+ if derived:
95
+ return True
96
+ return not bool(
97
+ getattr(storage, "nullable", True)
98
+ ) and not _storage_has_server_default(
99
+ spec,
100
+ storage,
101
+ )
102
+
103
+
104
+ def _ensure_temp(ctx: Any) -> dict[str, Any]:
105
+ tmp = getattr(ctx, "temp", None)
106
+ if not isinstance(tmp, dict):
107
+ tmp = {}
108
+ setattr(ctx, "temp", tmp)
109
+ return tmp
110
+
111
+
112
+ def _ensure_ov(ctx: Any):
113
+ ov = getattr(ctx, "opview", None)
114
+ if ov is None:
115
+ raise RuntimeError("ctx_missing:opview")
116
+ return ov
117
+
118
+
119
+ def _normalize_schema_from_specs(ctx: Any) -> None:
120
+ specs = getattr(ctx, "specs", None)
121
+ op = getattr(ctx, "op", None)
122
+ if not isinstance(specs, Mapping) or not isinstance(op, str) or not op:
123
+ raise RuntimeError("ctx_missing:opview")
124
+
125
+ in_fields: list[str] = []
126
+ out_fields: list[str] = []
127
+ stored_fields: list[str] = []
128
+ by_field_in: dict[str, dict[str, Any]] = {}
129
+ by_field_out: dict[str, dict[str, Any]] = {}
130
+ by_field_stored: dict[str, dict[str, Any]] = {}
131
+ required_from_client: list[str] = []
132
+
133
+ for field_name, spec in specs.items():
134
+ if not isinstance(field_name, str):
135
+ continue
136
+ io = getattr(spec, "io", None)
137
+ fs = getattr(spec, "field", None)
138
+ storage = getattr(spec, "storage", None)
139
+
140
+ in_verbs = set(getattr(io, "in_verbs", ()) or ())
141
+ out_verbs = set(getattr(io, "out_verbs", ()) or ())
142
+
143
+ if op in in_verbs:
144
+ in_fields.append(field_name)
145
+ in_meta: dict[str, Any] = {"in_enabled": True}
146
+ if storage is None:
147
+ in_meta["virtual"] = True
148
+ py_type = _spec_python_type(spec)
149
+ if py_type is not None and py_type is not Any:
150
+ in_meta["py_type"] = py_type
151
+ constraints = getattr(fs, "constraints", {}) or {}
152
+ if isinstance(constraints, Mapping):
153
+ max_length = constraints.get("max_length")
154
+ if isinstance(max_length, int) and max_length > 0:
155
+ in_meta["max_length"] = max_length
156
+
157
+ default_factory = getattr(spec, "default_factory", None)
158
+ if callable(default_factory):
159
+ in_meta["default_factory"] = default_factory
160
+
161
+ alias_in = getattr(io, "alias_in", None)
162
+ if alias_in:
163
+ in_meta["alias_in"] = alias_in
164
+
165
+ header_in = getattr(io, "header_in", None)
166
+ if header_in:
167
+ in_meta["header_in"] = header_in
168
+ in_meta["header_required_in"] = bool(
169
+ getattr(io, "header_required_in", False)
170
+ )
171
+
172
+ in_meta["required"] = bool(
173
+ (fs and op in getattr(fs, "required_in", ()))
174
+ or _storage_requires_input(storage, op)
175
+ )
176
+ in_meta["nullable"] = (
177
+ True if storage is None else bool(getattr(storage, "nullable", True))
178
+ )
179
+ in_meta["coerce"] = True
180
+ by_field_in[field_name] = in_meta
181
+
182
+ if op in out_verbs:
183
+ out_fields.append(field_name)
184
+ out_meta: dict[str, Any] = {}
185
+
186
+ alias_out = getattr(io, "alias_out", None)
187
+ if alias_out:
188
+ out_meta["alias_out"] = alias_out
189
+ if storage is None:
190
+ out_meta["virtual"] = True
191
+
192
+ py_type = getattr(getattr(fs, "py_type", None), "__name__", None)
193
+ if py_type:
194
+ out_meta["py_type"] = py_type
195
+ by_field_out[field_name] = out_meta
196
+
197
+ if _include_in_stored_schema(spec, io, storage, op):
198
+ stored_fields.append(field_name)
199
+ input_enabled = _is_input_enabled(io, op)
200
+ paired_enabled = _is_paired_enabled(io, op)
201
+ server_default = _storage_has_server_default(spec, storage)
202
+ py_type = getattr(fs, "py_type", None) if fs is not None else None
203
+ if py_type is not None and py_type is not Any:
204
+ resolved_py_type = py_type
205
+ else:
206
+ storage_type = getattr(storage, "type_", None)
207
+ resolved_py_type = (
208
+ Any
209
+ if storage_type is None
210
+ else getattr(storage_type, "python_type", Any)
211
+ )
212
+ stored_meta: dict[str, Any] = {
213
+ "nullable": bool(getattr(storage, "nullable", True)),
214
+ "from_client": input_enabled,
215
+ "required_from_client": bool(
216
+ input_enabled and _storage_requires_input(storage, op)
217
+ ),
218
+ "server_default": server_default,
219
+ }
220
+ if resolved_py_type is not Any:
221
+ stored_meta["py_type"] = resolved_py_type
222
+ constraints = getattr(fs, "constraints", {}) or {}
223
+ if isinstance(constraints, Mapping):
224
+ max_length = constraints.get("max_length")
225
+ if isinstance(max_length, int) and max_length > 0:
226
+ stored_meta["max_length"] = max_length
227
+ derived = paired_enabled or callable(
228
+ getattr(getattr(storage, "transform", None), "to_stored", None)
229
+ )
230
+ stored_meta["derived"] = derived
231
+ stored_meta["required"] = _stored_required(
232
+ spec,
233
+ storage,
234
+ op,
235
+ derived=derived,
236
+ )
237
+ if stored_meta["required_from_client"]:
238
+ required_from_client.append(field_name)
239
+ by_field_stored[field_name] = stored_meta
240
+
241
+ target = str(getattr(ctx, "target", None) or op)
242
+ for name, meta in {
243
+ **_input_filter_entries(specs, target),
244
+ **_input_selector_entries(specs, target),
245
+ }.items():
246
+ if name not in by_field_in:
247
+ in_fields.append(name)
248
+ by_field_in[name] = meta
249
+ elif meta.get("selector"):
250
+ by_field_in[name] = {**meta, **by_field_in[name], "selector": True}
251
+
252
+ in_fields_sorted = tuple(sorted(in_fields))
253
+ out_fields_sorted = tuple(sorted(out_fields))
254
+ stored_fields_sorted = tuple(sorted(stored_fields))
255
+
256
+ setattr(
257
+ ctx,
258
+ "schema_in",
259
+ {
260
+ "fields": in_fields_sorted,
261
+ "by_field": {f: by_field_in.get(f, {}) for f in in_fields_sorted},
262
+ "required": tuple(
263
+ f for f in in_fields_sorted if by_field_in.get(f, {}).get("required")
264
+ ),
265
+ },
266
+ )
267
+ setattr(
268
+ ctx,
269
+ "schema_out",
270
+ {
271
+ "fields": out_fields_sorted,
272
+ "by_field": {f: by_field_out.get(f, {}) for f in out_fields_sorted},
273
+ "expose": out_fields_sorted,
274
+ },
275
+ )
276
+ setattr(
277
+ ctx,
278
+ "schema_stored",
279
+ {
280
+ "fields": stored_fields_sorted,
281
+ "by_field": {f: by_field_stored.get(f, {}) for f in stored_fields_sorted},
282
+ "required_from_client": tuple(sorted(required_from_client)),
283
+ "required": tuple(
284
+ f
285
+ for f in stored_fields_sorted
286
+ if by_field_stored.get(f, {}).get("required")
287
+ ),
288
+ },
289
+ )
290
+
291
+
292
+ def _ensure_schema_in(ctx: Any) -> Mapping[str, Any]:
293
+ temp = _ensure_temp(ctx)
294
+ cached = temp.get("schema_in")
295
+ if isinstance(cached, Mapping):
296
+ return cached
297
+
298
+ schema_in = getattr(ctx, "schema_in", None)
299
+ if isinstance(schema_in, Mapping):
300
+ temp["schema_in"] = schema_in
301
+ return schema_in
302
+
303
+ try:
304
+ ov = _ensure_ov(ctx)
305
+ bf = ov.schema_in.by_field
306
+ req = tuple(n for n, e in bf.items() if e.get("required"))
307
+ temp["schema_in"] = {
308
+ "fields": ov.schema_in.fields,
309
+ "by_field": bf,
310
+ "required": req,
311
+ }
312
+ except RuntimeError as exc:
313
+ if str(exc) != "ctx_missing:opview":
314
+ raise
315
+ _normalize_schema_from_specs(ctx)
316
+ temp["schema_in"] = getattr(ctx, "schema_in")
317
+ return temp["schema_in"]
318
+
319
+
320
+ def _ensure_schema_out(ctx: Any) -> Mapping[str, Any]:
321
+ temp = _ensure_temp(ctx)
322
+ cached = temp.get("schema_out")
323
+ if isinstance(cached, Mapping):
324
+ return cached
325
+
326
+ schema_out = getattr(ctx, "schema_out", None)
327
+ if isinstance(schema_out, Mapping):
328
+ temp["schema_out"] = schema_out
329
+ return schema_out
330
+
331
+ try:
332
+ ov = _ensure_ov(ctx)
333
+ temp["schema_out"] = {
334
+ "fields": ov.schema_out.fields,
335
+ "by_field": ov.schema_out.by_field,
336
+ "expose": ov.schema_out.expose,
337
+ }
338
+ except RuntimeError as exc:
339
+ if str(exc) != "ctx_missing:opview":
340
+ raise
341
+ _normalize_schema_from_specs(ctx)
342
+ temp["schema_out"] = getattr(ctx, "schema_out")
343
+ return temp["schema_out"]
344
+
345
+
346
+ def _ensure_schema_stored(ctx: Any) -> Mapping[str, Any]:
347
+ temp = _ensure_temp(ctx)
348
+ cached = temp.get("schema_stored")
349
+ if isinstance(cached, Mapping):
350
+ return cached
351
+
352
+ schema_stored = getattr(ctx, "schema_stored", None)
353
+ if isinstance(schema_stored, Mapping):
354
+ temp["schema_stored"] = schema_stored
355
+ return schema_stored
356
+
357
+ try:
358
+ ov = _ensure_ov(ctx)
359
+ stored = getattr(ov, "schema_stored", None)
360
+ if stored is None:
361
+ raise RuntimeError("ctx_missing:opview")
362
+ temp["schema_stored"] = {
363
+ "fields": stored.fields,
364
+ "by_field": stored.by_field,
365
+ "required_from_client": getattr(stored, "required_from_client", ()),
366
+ "required": tuple(
367
+ field
368
+ for field, meta in getattr(stored, "by_field", {}).items()
369
+ if meta.get("required")
370
+ ),
371
+ }
372
+ except RuntimeError as exc:
373
+ if str(exc) != "ctx_missing:opview":
374
+ raise
375
+ _normalize_schema_from_specs(ctx)
376
+ temp["schema_stored"] = getattr(ctx, "schema_stored")
377
+ return temp["schema_stored"]
@@ -20,22 +20,34 @@ from .codec import (
20
20
  get_frame_codec,
21
21
  supported_frame_codecs,
22
22
  )
23
+ from . import websocket_jsonrpc as websocket_jsonrpc
24
+ from .webtransport_records import (
25
+ DEFAULT_MAX_RECORD_BYTES,
26
+ WebTransportRecordDecoder,
27
+ decode_jsonrpc_records,
28
+ encode_jsonrpc_record,
29
+ )
23
30
 
24
31
  __all__ = [
25
32
  "DEFAULT_MAX_PAYLOAD_SIZE",
33
+ "DEFAULT_MAX_RECORD_BYTES",
26
34
  "FRAME_CODECS",
27
35
  "HEADER_SIZE",
28
36
  "RESERVED_FLAG_MASK",
29
37
  "SUPPORTED_VERSION",
30
38
  "FrameCodec",
31
39
  "FrameStreamDecoder",
40
+ "WebTransportRecordDecoder",
32
41
  "decode_app_frame",
33
42
  "decode_app_frames",
34
43
  "decode_frame",
44
+ "decode_jsonrpc_records",
35
45
  "decode_webtransport_inner_frame",
36
46
  "encode_app_frame",
37
47
  "encode_frame",
48
+ "encode_jsonrpc_record",
38
49
  "encode_webtransport_inner_frame",
39
50
  "get_frame_codec",
40
51
  "supported_frame_codecs",
52
+ "websocket_jsonrpc",
41
53
  ]
@@ -0,0 +1,59 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any
4
+
5
+ from .codec import decode_frame, encode_frame
6
+
7
+
8
+ def _ctx_get(ctx: Any, key: str, default: Any = None) -> Any:
9
+ getter = getattr(ctx, "get", None)
10
+ if callable(getter):
11
+ try:
12
+ return getter(key, default)
13
+ except Exception:
14
+ pass
15
+ return getattr(ctx, key, default)
16
+
17
+
18
+ def _ctx_set(ctx: Any, key: str, value: Any) -> None:
19
+ try:
20
+ ctx[key] = value
21
+ return
22
+ except Exception:
23
+ pass
24
+ setattr(ctx, key, value)
25
+
26
+
27
+ def _temp(ctx: Any) -> dict[str, Any]:
28
+ temp = _ctx_get(ctx, "temp")
29
+ if not isinstance(temp, dict):
30
+ temp = {}
31
+ _ctx_set(ctx, "temp", temp)
32
+ return temp
33
+
34
+
35
+ def _ws_state(ctx: Any) -> dict[str, Any]:
36
+ return _temp(ctx).setdefault("websocket", {})
37
+
38
+
39
+ async def decode(ctx: Any) -> Any:
40
+ state = _ws_state(ctx)
41
+ payload = decode_frame("websocket.jsonrpc", state.get("message"))
42
+ state["decoded"] = payload
43
+ return payload
44
+
45
+
46
+ async def encode(ctx: Any) -> Any:
47
+ state = _ws_state(ctx)
48
+ outbound = encode_frame("websocket.jsonrpc", state.get("response"))
49
+ state["outbound"] = outbound
50
+ return outbound
51
+
52
+
53
+ setattr(decode, "__tigrbl_atom_name__", "framing.decode")
54
+ setattr(decode, "__tigrbl_label", "atom:framing.decode")
55
+ setattr(encode, "__tigrbl_atom_name__", "framing.encode")
56
+ setattr(encode, "__tigrbl_label", "atom:framing.encode")
57
+
58
+
59
+ __all__ = ["decode", "encode"]