spatialdds 0.7.3__tar.gz → 0.7.4__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 (245) hide show
  1. {spatialdds-0.7.3 → spatialdds-0.7.4}/PKG-INFO +19 -3
  2. {spatialdds-0.7.3 → spatialdds-0.7.4}/README.md +18 -2
  3. {spatialdds-0.7.3 → spatialdds-0.7.4}/pyproject.toml +1 -1
  4. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/__init__.py +1 -1
  5. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/openapi.html +1 -1
  6. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/openapi.yaml +1 -1
  7. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/dds/__init__.py +2 -1
  8. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/dds/_participant.py +56 -8
  9. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/dds/codec.py +8 -1
  10. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/dds/record.py +5 -2
  11. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/records.py +9 -1
  12. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/serve.py +6 -1
  13. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/server/gateway.py +19 -2
  14. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/server/gw_http.py +13 -2
  15. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/server/idlmodel.py +36 -0
  16. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/server/mcapmeta.py +5 -0
  17. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/server/pin.py +1 -0
  18. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/server/webjson.py +127 -1
  19. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds.egg-info/PKG-INFO +19 -3
  20. {spatialdds-0.7.3 → spatialdds-0.7.4}/setup.cfg +0 -0
  21. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/idl/anchors.idl +0 -0
  22. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/idl/argeo.idl +0 -0
  23. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/idl/common.idl +0 -0
  24. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/idl/core.idl +0 -0
  25. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/idl/discovery.idl +0 -0
  26. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/idl/events.idl +0 -0
  27. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/idl/lidar.idl +0 -0
  28. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/idl/mapping.idl +0 -0
  29. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/idl/provisional/owm.idl +0 -0
  30. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/idl/provisional/radio.idl +0 -0
  31. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/idl/provisional/rf_beam.idl +0 -0
  32. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/idl/rad.idl +0 -0
  33. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/idl/semantics.idl +0 -0
  34. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/idl/slam_frontend.idl +0 -0
  35. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/idl/types.idl +0 -0
  36. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/idl/vio.idl +0 -0
  37. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/idl/vision.idl +0 -0
  38. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/registry.json +0 -0
  39. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/builtin_Time.json +0 -0
  40. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_anchors_AnchorDelta.json +0 -0
  41. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_anchors_AnchorEntry.json +0 -0
  42. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_anchors_AnchorSet.json +0 -0
  43. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_anchors_AnchorSetRequest.json +0 -0
  44. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_anchors_AnchorSetResponse.json +0 -0
  45. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_argeo_NodeGeo.json +0 -0
  46. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_argeo_QualityRequirements.json +0 -0
  47. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_argeo_VpsRequest.json +0 -0
  48. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_argeo_VpsResponse.json +0 -0
  49. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_common_AssetRef.json +0 -0
  50. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_common_FrameRef.json +0 -0
  51. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_common_KV.json +0 -0
  52. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_common_MetaKV.json +0 -0
  53. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_common_Vec2.json +0 -0
  54. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_core_Aabb3.json +0 -0
  55. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_core_BlobChunk.json +0 -0
  56. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_core_BlobRef.json +0 -0
  57. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_core_ComponentRef.json +0 -0
  58. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_core_Edge.json +0 -0
  59. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_core_EntityBinding.json +0 -0
  60. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_core_FrameTransform.json +0 -0
  61. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_core_FramedPose.json +0 -0
  62. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_core_GeoAnchor.json +0 -0
  63. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_core_GeoPose.json +0 -0
  64. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_core_NavSatStatus.json +0 -0
  65. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_core_Node.json +0 -0
  66. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_core_PlannedTrajectory.json +0 -0
  67. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_core_PlannedWaypoint.json +0 -0
  68. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_core_PoseSE3.json +0 -0
  69. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_core_SnapshotRequest.json +0 -0
  70. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_core_SnapshotResponse.json +0 -0
  71. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_core_TileKey.json +0 -0
  72. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_core_TileMeta.json +0 -0
  73. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_core_TilePatch.json +0 -0
  74. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_disco_Announce.json +0 -0
  75. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_disco_Capabilities.json +0 -0
  76. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_disco_ContentAnnounce.json +0 -0
  77. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_disco_CoverageElement.json +0 -0
  78. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_disco_CoverageFilter.json +0 -0
  79. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_disco_CoverageHint.json +0 -0
  80. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_disco_CoverageQuery.json +0 -0
  81. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_disco_CoverageResponse.json +0 -0
  82. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_disco_Depart.json +0 -0
  83. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_disco_KV.json +0 -0
  84. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_disco_ProfileSupport.json +0 -0
  85. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_disco_ServiceSummary.json +0 -0
  86. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_disco_TopicMeta.json +0 -0
  87. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_disco_Transform.json +0 -0
  88. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_disco_ValidityWindow.json +0 -0
  89. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_events_CrossingLine.json +0 -0
  90. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_events_SpatialEvent.json +0 -0
  91. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_events_SpatialZone.json +0 -0
  92. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_events_ZoneState.json +0 -0
  93. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_mapping_Edge.json +0 -0
  94. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_mapping_MapAlignment.json +0 -0
  95. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_mapping_MapEvent.json +0 -0
  96. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_mapping_MapMeta.json +0 -0
  97. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_mapping_MapQuality.json +0 -0
  98. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_owm_Entity.json +0 -0
  99. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_owm_ModelCommand.json +0 -0
  100. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_owm_ModelPose.json +0 -0
  101. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_semantics_Detection2D.json +0 -0
  102. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_semantics_Detection2DSet.json +0 -0
  103. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_semantics_Detection3D.json +0 -0
  104. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_semantics_Detection3DSet.json +0 -0
  105. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_semantics_FusedTrack.json +0 -0
  106. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_semantics_FusedTrackSet.json +0 -0
  107. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_semantics_Keypoint3D.json +0 -0
  108. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_semantics_KeypointLink.json +0 -0
  109. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_sensing_common_Axis.json +0 -0
  110. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_sensing_common_FrameHeader.json +0 -0
  111. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_sensing_common_FrameQuality.json +0 -0
  112. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_sensing_common_Linspace.json +0 -0
  113. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_sensing_common_ROI.json +0 -0
  114. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_sensing_common_ROIReply.json +0 -0
  115. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_sensing_common_ROIRequest.json +0 -0
  116. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_sensing_common_StreamMeta.json +0 -0
  117. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_sensing_lidar_LidarDetection.json +0 -0
  118. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_sensing_lidar_LidarDetectionSet.json +0 -0
  119. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_sensing_lidar_LidarFrame.json +0 -0
  120. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_sensing_lidar_LidarMeta.json +0 -0
  121. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_sensing_rad_RadDetection.json +0 -0
  122. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_sensing_rad_RadDetectionSet.json +0 -0
  123. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_sensing_rad_RadSensorMeta.json +0 -0
  124. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_sensing_rad_RadTensorFrame.json +0 -0
  125. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_sensing_rad_RadTensorMeta.json +0 -0
  126. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_sensing_radio_RadioObservation.json +0 -0
  127. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_sensing_radio_RadioScan.json +0 -0
  128. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_sensing_radio_RadioSensorMeta.json +0 -0
  129. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_sensing_rf_beam_RfBeamArraySet.json +0 -0
  130. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_sensing_rf_beam_RfBeamFrame.json +0 -0
  131. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_sensing_rf_beam_RfBeamMeta.json +0 -0
  132. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_sensing_vision_CamIntrinsics.json +0 -0
  133. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_sensing_vision_Keypoint2D.json +0 -0
  134. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_sensing_vision_Track2D.json +0 -0
  135. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_sensing_vision_VisionDetections.json +0 -0
  136. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_sensing_vision_VisionFrame.json +0 -0
  137. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_sensing_vision_VisionMeta.json +0 -0
  138. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_slam_frontend_CameraInfo.json +0 -0
  139. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_slam_frontend_Feature2D.json +0 -0
  140. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_slam_frontend_FeatureMatch.json +0 -0
  141. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_slam_frontend_KeyframeFeatures.json +0 -0
  142. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_slam_frontend_Landmark.json +0 -0
  143. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_slam_frontend_MatchSet.json +0 -0
  144. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_slam_frontend_TrackObs.json +0 -0
  145. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_slam_frontend_Tracklet.json +0 -0
  146. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_vio_FusedState.json +0 -0
  147. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_vio_ImuInfo.json +0 -0
  148. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_vio_ImuSample.json +0 -0
  149. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_vio_MagnetometerSample.json +0 -0
  150. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/schemas/spatial_vio_SensorFusionSample.json +0 -0
  151. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/artifacts/serializer_tables.json +0 -0
  152. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/canonical.py +0 -0
  153. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/canonjson.py +0 -0
  154. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/client.py +0 -0
  155. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/dds/_extra.py +0 -0
  156. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/dds/blob.py +0 -0
  157. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/dds/qos.py +0 -0
  158. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/dds/registry.py +0 -0
  159. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/dds/types/__init__.py +0 -0
  160. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/dds/types/_field_aliases.py +0 -0
  161. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/dds/types/builtin/__init__.py +0 -0
  162. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/dds/types/builtin/_types.py +0 -0
  163. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/dds/types/spatial/__init__.py +0 -0
  164. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/dds/types/spatial/anchors/__init__.py +0 -0
  165. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/dds/types/spatial/anchors/_anchors.py +0 -0
  166. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/dds/types/spatial/argeo/__init__.py +0 -0
  167. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/dds/types/spatial/argeo/_argeo.py +0 -0
  168. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/dds/types/spatial/common/__init__.py +0 -0
  169. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/dds/types/spatial/common/_types.py +0 -0
  170. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/dds/types/spatial/core/GnssService/__init__.py +0 -0
  171. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/dds/types/spatial/core/GnssService/_core.py +0 -0
  172. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/dds/types/spatial/core/__init__.py +0 -0
  173. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/dds/types/spatial/core/_core.py +0 -0
  174. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/dds/types/spatial/disco/__init__.py +0 -0
  175. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/dds/types/spatial/disco/_discovery.py +0 -0
  176. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/dds/types/spatial/events/__init__.py +0 -0
  177. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/dds/types/spatial/events/_events.py +0 -0
  178. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/dds/types/spatial/events/enums/__init__.py +0 -0
  179. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/dds/types/spatial/events/enums/crossing_direction_enum/__init__.py +0 -0
  180. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/dds/types/spatial/events/enums/crossing_direction_enum/_events.py +0 -0
  181. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/dds/types/spatial/events/enums/event_state_enum/__init__.py +0 -0
  182. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/dds/types/spatial/events/enums/event_state_enum/_events.py +0 -0
  183. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/dds/types/spatial/events/enums/event_type_enum/__init__.py +0 -0
  184. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/dds/types/spatial/events/enums/event_type_enum/_events.py +0 -0
  185. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/dds/types/spatial/events/enums/severity_enum/__init__.py +0 -0
  186. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/dds/types/spatial/events/enums/severity_enum/_events.py +0 -0
  187. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/dds/types/spatial/events/enums/zone_kind_enum/__init__.py +0 -0
  188. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/dds/types/spatial/events/enums/zone_kind_enum/_events.py +0 -0
  189. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/dds/types/spatial/mapping/__init__.py +0 -0
  190. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/dds/types/spatial/mapping/_mapping.py +0 -0
  191. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/dds/types/spatial/mapping/enums/__init__.py +0 -0
  192. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/dds/types/spatial/mapping/enums/alignment_method/__init__.py +0 -0
  193. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/dds/types/spatial/mapping/enums/alignment_method/_mapping.py +0 -0
  194. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/dds/types/spatial/mapping/enums/edge_type/__init__.py +0 -0
  195. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/dds/types/spatial/mapping/enums/edge_type/_mapping.py +0 -0
  196. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/dds/types/spatial/mapping/enums/map_event_kind/__init__.py +0 -0
  197. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/dds/types/spatial/mapping/enums/map_event_kind/_mapping.py +0 -0
  198. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/dds/types/spatial/mapping/enums/map_kind/__init__.py +0 -0
  199. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/dds/types/spatial/mapping/enums/map_kind/_mapping.py +0 -0
  200. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/dds/types/spatial/mapping/enums/map_status/__init__.py +0 -0
  201. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/dds/types/spatial/mapping/enums/map_status/_mapping.py +0 -0
  202. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/dds/types/spatial/owm/__init__.py +0 -0
  203. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/dds/types/spatial/owm/_owm.py +0 -0
  204. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/dds/types/spatial/semantics/__init__.py +0 -0
  205. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/dds/types/spatial/semantics/_semantics.py +0 -0
  206. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/dds/types/spatial/sensing/__init__.py +0 -0
  207. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/dds/types/spatial/sensing/common/__init__.py +0 -0
  208. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/dds/types/spatial/sensing/common/_common.py +0 -0
  209. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/dds/types/spatial/sensing/lidar/__init__.py +0 -0
  210. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/dds/types/spatial/sensing/lidar/_lidar.py +0 -0
  211. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/dds/types/spatial/sensing/rad/__init__.py +0 -0
  212. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/dds/types/spatial/sensing/rad/_rad.py +0 -0
  213. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/dds/types/spatial/sensing/radio/__init__.py +0 -0
  214. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/dds/types/spatial/sensing/radio/_radio.py +0 -0
  215. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/dds/types/spatial/sensing/rf_beam/__init__.py +0 -0
  216. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/dds/types/spatial/sensing/rf_beam/_rf_beam.py +0 -0
  217. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/dds/types/spatial/sensing/vision/__init__.py +0 -0
  218. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/dds/types/spatial/sensing/vision/_vision.py +0 -0
  219. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/dds/types/spatial/slam_frontend/__init__.py +0 -0
  220. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/dds/types/spatial/slam_frontend/_slam_frontend.py +0 -0
  221. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/dds/types/spatial/vio/__init__.py +0 -0
  222. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/dds/types/spatial/vio/_vio.py +0 -0
  223. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/demo/assets/duck.gltf +0 -0
  224. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/demo/assets/fountain-label.txt +0 -0
  225. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/demo/assets/fountain-marker.gltf +0 -0
  226. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/demo/fountain-anchors.mcap +0 -0
  227. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/demo/manifests/content-fountain-label.json +0 -0
  228. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/demo/manifests/content-fountain-marker.json +0 -0
  229. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/errors.py +0 -0
  230. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/events.py +0 -0
  231. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/frames.py +0 -0
  232. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/journal.py +0 -0
  233. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/recency.py +0 -0
  234. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/server/__init__.py +0 -0
  235. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/server/commands.py +0 -0
  236. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/server/limits.py +0 -0
  237. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/server/publish.py +0 -0
  238. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/server/tenancy.py +0 -0
  239. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/server/xcdr2.py +0 -0
  240. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds/tokens.py +0 -0
  241. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds.egg-info/SOURCES.txt +0 -0
  242. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds.egg-info/dependency_links.txt +0 -0
  243. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds.egg-info/entry_points.txt +0 -0
  244. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds.egg-info/requires.txt +0 -0
  245. {spatialdds-0.7.3 → spatialdds-0.7.4}/spatialdds.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: spatialdds
3
- Version: 0.7.3
3
+ Version: 0.7.4
4
4
  Summary: SpatialDDS for programs: a client for the web binding (Appendix N), a native DDS participant, and the reference registry behind one command.
5
5
  Author: Open AR Cloud
6
6
  License: Apache-2.0
@@ -114,7 +114,7 @@ What it asks of you, and what it will not guess:
114
114
  | `service_id` and `authority` | to write or announce |
115
115
  | `qos_profile` | where the §3.3.2 row names none, as `model_entity`'s and `model_pose`'s do; the refusal lists the §3.3.3 profiles that fit, or pass a `sd.dds.Profile(...)` extension |
116
116
  | `on_exit="keep"` or `"withdraw"` | on every latched, keyed writer: whether a record outlives its writer is a claim only you can make |
117
- | `withdraw(key, reason=...)` | with an empty reason |
117
+ | `withdraw(key, reason=..., **changes)` | with an empty reason, or a change to the members it sets itself or to the key |
118
118
 
119
119
  And what it does without being asked:
120
120
 
@@ -602,6 +602,22 @@ explicitly was tried first and is what missed two paths.
602
602
 
603
603
  If your clients are not browsers, nothing here changes anything for you.
604
604
 
605
+ ## New in 0.7.4: what the first extension found
606
+
607
+ A deployment's own type, registered with `sd.dds.extend()`, is now served
608
+ the way a pinned one is. The gateway publishes a schema for it, written from
609
+ the model it decodes the type with by the specification generator's rules,
610
+ because N.2 requires a schema for every type an endpoint serves. A
611
+ recording's extension lane is served latched when its writer opened it so,
612
+ and `serve --public FQN` opens it to uncredentialed readers.
613
+
614
+ - `withdraw(key, reason=..., **changes)` sets other members on the terminal
615
+ sample, such as a closed validity interval.
616
+ - `close()` finishes when a reader is slow to acknowledge (cyclonedds-python
617
+ 11.0.1 raised on that timeout).
618
+ - `sd.stamp` keeps `nanosec` in range, and floors a time before the epoch.
619
+ - `sd.dds.extensions()` and `sd.dds.extension_of()` are exported.
620
+
605
621
  ## New in 0.7.3: what the first migration found
606
622
 
607
623
  Mostly additive; one fix changes bytes. Every frame this package builds now
@@ -679,7 +695,7 @@ Two changes may need a line of yours:
679
695
  The security fixes, the limits table and the details are in the
680
696
  [changelog](https://github.com/OpenArCloud/spatialdds-py/blob/main/lib/spatialdds/CHANGELOG.md).
681
697
 
682
- ## Not in 0.7.3
698
+ ## Not in 0.7.4
683
699
 
684
700
  Stated so nobody waits for them:
685
701
 
@@ -86,7 +86,7 @@ What it asks of you, and what it will not guess:
86
86
  | `service_id` and `authority` | to write or announce |
87
87
  | `qos_profile` | where the §3.3.2 row names none, as `model_entity`'s and `model_pose`'s do; the refusal lists the §3.3.3 profiles that fit, or pass a `sd.dds.Profile(...)` extension |
88
88
  | `on_exit="keep"` or `"withdraw"` | on every latched, keyed writer: whether a record outlives its writer is a claim only you can make |
89
- | `withdraw(key, reason=...)` | with an empty reason |
89
+ | `withdraw(key, reason=..., **changes)` | with an empty reason, or a change to the members it sets itself or to the key |
90
90
 
91
91
  And what it does without being asked:
92
92
 
@@ -574,6 +574,22 @@ explicitly was tried first and is what missed two paths.
574
574
 
575
575
  If your clients are not browsers, nothing here changes anything for you.
576
576
 
577
+ ## New in 0.7.4: what the first extension found
578
+
579
+ A deployment's own type, registered with `sd.dds.extend()`, is now served
580
+ the way a pinned one is. The gateway publishes a schema for it, written from
581
+ the model it decodes the type with by the specification generator's rules,
582
+ because N.2 requires a schema for every type an endpoint serves. A
583
+ recording's extension lane is served latched when its writer opened it so,
584
+ and `serve --public FQN` opens it to uncredentialed readers.
585
+
586
+ - `withdraw(key, reason=..., **changes)` sets other members on the terminal
587
+ sample, such as a closed validity interval.
588
+ - `close()` finishes when a reader is slow to acknowledge (cyclonedds-python
589
+ 11.0.1 raised on that timeout).
590
+ - `sd.stamp` keeps `nanosec` in range, and floors a time before the epoch.
591
+ - `sd.dds.extensions()` and `sd.dds.extension_of()` are exported.
592
+
577
593
  ## New in 0.7.3: what the first migration found
578
594
 
579
595
  Mostly additive; one fix changes bytes. Every frame this package builds now
@@ -651,7 +667,7 @@ Two changes may need a line of yours:
651
667
  The security fixes, the limits table and the details are in the
652
668
  [changelog](https://github.com/OpenArCloud/spatialdds-py/blob/main/lib/spatialdds/CHANGELOG.md).
653
669
 
654
- ## Not in 0.7.3
670
+ ## Not in 0.7.4
655
671
 
656
672
  Stated so nobody waits for them:
657
673
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "spatialdds"
7
- version = "0.7.3"
7
+ version = "0.7.4"
8
8
  description = "SpatialDDS for programs: a client for the web binding (Appendix N), a native DDS participant, and the reference registry behind one command."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
@@ -41,7 +41,7 @@ __all__ = [
41
41
  "participant", "MissingExtra", "dds", "events", "frames", "Recency",
42
42
  ]
43
43
 
44
- __version__ = "0.7.3"
44
+ __version__ = "0.7.4"
45
45
  #: The SpatialDDS-spec commit whose Appendix N and generated artifacts this
46
46
  #: package implements. Package versions and specification versions move
47
47
  #: independently; this is the one that says what the wire looks like.
@@ -14,4 +14,4 @@ border-bottom:1px solid var(--line)}p{margin:.35rem 0}.mut,.sum .auth{color:
14
14
  var(--mut)}.auth{font-size:.85rem;margin-left:.4rem}.v{display:inline-block;
15
15
  min-width:4.2rem;font:600 12px ui-monospace,monospace;letter-spacing:.04em;
16
16
  color:var(--acc)}.codes{font:12px ui-monospace,monospace;color:var(--mut)}
17
- .s{margin-right:.5rem}.s4,.s5{color:#b5482c}a{color:var(--acc)}</style></head><body><main><h1>SpatialDDS web binding (spatialdds-py)</h1><p class=mut>Version 0.7.3 · OpenAPI 3.1.0 · generated, never hand-written · <a href='openapi'>openapi.yaml</a></p><p>Generated by <code>tools/gen_openapi.py</code> from the gateway&#x27;s own tables and the published JSON Schemas; never edited by hand. Reads are open; writes take a bearer token scoped to scenes. The WebSocket streams (N.5) are described on each collection path, not modelled.</p><h2>Operations</h2><section id="search"><h3><span class="v get">GET</span> <code>/.well-known/spatialdds/search</code></h3><p class="sum">Services near a geohash (§8.2.3 manifests) <span class="auth">open</span></p><p class="codes"><span class="s s2">200</span> <span class="s s4">429</span></p></section><section id="searchPost"><h3><span class="v post">POST</span> <code>/.well-known/spatialdds/search</code></h3><p class="sum">The same search, as a POST body <span class="auth">open</span></p><p class="codes"><span class="s s2">200</span> <span class="s s4">429</span> <span class="s s4">413</span> <span class="s s4">415</span></p></section><section id="listSchemas"><h3><span class="v get">GET</span> <code>/.well-known/spatialdds/schemas</code></h3><p class="sum">The published JSON Schemas, by type <span class="auth">open</span></p><p class="codes"><span class="s s2">200</span> <span class="s s4">429</span></p></section><section id="getSchema"><h3><span class="v get">GET</span> <code>/.well-known/spatialdds/schemas/{type}</code></h3><p class="sum"> <span class="auth">open</span></p><p class="codes"><span class="s s2">200</span> <span class="s s4">404</span> <span class="s s4">429</span></p></section><section id="bootstrap"><h3><span class="v get">GET</span> <code>/.well-known/spatialdds/bootstrap</code></h3><p class="sum">DDS-join material (N.2); optional <span class="auth">open</span></p><p class="codes"><span class="s s2">200</span> <span class="s s4">404</span> <span class="s s4">429</span></p></section><section id="resolverMetadata"><h3><span class="v get">GET</span> <code>/.well-known/spatialdds/resolver</code></h3><p class="sum">§7.5.2 resolver metadata <span class="auth">open</span></p><p class="codes"><span class="s s2">200</span> <span class="s s4">404</span> <span class="s s4">429</span></p></section><section id="openapi"><h3><span class="v get">GET</span> <code>/.well-known/spatialdds/openapi</code></h3><p class="sum">This document <span class="auth">open</span></p><p class="codes"><span class="s s2">200</span> <span class="s s4">429</span></p></section><section id="openapiPage"><h3><span class="v get">GET</span> <code>/.well-known/spatialdds/openapi.html</code></h3><p class="sum">This document as a static reference page <span class="auth">open</span></p><p class="codes"><span class="s s2">200</span> <span class="s s4">429</span></p></section><section id="resolve"><h3><span class="v get">GET</span> <code>/spatialdds/resolve</code></h3><p class="sum">Resolve a URI to its manifest (§7.5.2) <span class="auth">open</span></p><p class="codes"><span class="s s2">200</span> <span class="s s4">400</span> <span class="s s4">404</span> <span class="s s4">429</span></p></section><section id="putManifest"><h3><span class="v put">PUT</span> <code>/spatialdds/resolve</code></h3><p class="sum">Publish a manifest, keyed by its own <code>id</code> <span class="auth">token</span></p><p>The <code>uri</code> parameter is not used; the body&#x27;s <code>id</code> is the key. Its scene segment is the lane. **Writes are per lane.** The path&#x27;s first segment is the scene; the bearer token must be granted it (<code>tenancy.WRITE_REFUSALS</code>).</p><p class="codes"><span class="s s2">200</span> <span class="s s2">201</span> <span class="s s4">400</span> <span class="s s4">401</span> <span class="s s4">403</span> <span class="s s4">405</span> <span class="s s4">429</span> <span class="s s4">413</span> <span class="s s4">415</span> <span class="s s5">503</span></p></section><section id="withdrawManifest"><h3><span class="v delete">DELETE</span> <code>/spatialdds/resolve</code></h3><p class="sum">Withdraw a manifest; <code>resolve</code> then answers 404 <span class="auth">token</span></p><p>Owner-checked by the URI&#x27;s scene segment. **Writes are per lane.** The path&#x27;s first segment is the scene; the bearer token must be granted it (<code>tenancy.WRITE_REFUSALS</code>).</p><p class="codes"><span class="s s2">204</span> <span class="s s4">400</span> <span class="s s4">401</span> <span class="s s4">403</span> <span class="s s4">404</span> <span class="s s4">405</span> <span class="s s4">429</span> <span class="s s5">503</span></p></section><section id="getAsset"><h3><span class="v get">GET</span> <code>/assets/{name}</code></h3><p class="sum">Asset bytes this endpoint hosts (implementation-defined) <span class="auth">open</span></p><p class="codes"><span class="s s2">200</span> <span class="s s4">404</span> <span class="s s4">429</span></p></section><section id="listAnnounce"><h3><span class="v get">GET</span> <code>/{scene}/{stream}/announce/v1</code></h3><p class="sum">Every spatial.disco.Announce instance in the latched set <span class="auth">open</span></p><p>**The same URL is also a WebSocket** (N.5): connect with <code>wss://</code> and receive the latched set as <code>sample</code> events, exactly one <code>end_of_replay</code> carrying the replay count, then live <code>sample</code> and <code>dispose</code> events (<code>{event, topic, key, data | reason}</code>). Not modelled here; see [Appendix N.5](https://spatialdds.org/v1.8/appendix-n/).</p><p class="codes"><span class="s s2">200</span> <span class="s s3">304</span> <span class="s s4">404</span> <span class="s s4">429</span> <span class="s s5">503</span></p></section><section id="commandAnnounce"><h3><span class="v post">POST</span> <code>/{scene}/{stream}/announce/v1</code></h3><p class="sum">An N.6 command addressed through the announce lane <span class="auth">token</span></p><p>Accepted only on a lane whose topic advertises <code>accepts_commands</code>; otherwise <code>405</code>. **Writes are per lane.** The path&#x27;s first segment is the scene; the bearer token must be granted it (<code>tenancy.WRITE_REFUSALS</code>). Every member of the record is required: a body is a whole record, never a patch.</p><p class="codes"><span class="s s2">202</span> <span class="s s4">400</span> <span class="s s4">401</span> <span class="s s4">403</span> <span class="s s4">405</span> <span class="s s4">409</span> <span class="s s4">429</span> <span class="s s4">413</span> <span class="s s4">415</span></p></section><section id="getAnnounce"><h3><span class="v get">GET</span> <code>/{scene}/{stream}/announce/v1/{key}</code></h3><p class="sum">One spatial.disco.Announce instance <span class="auth">open</span></p><p class="codes"><span class="s s2">200</span> <span class="s s3">304</span> <span class="s s4">404</span> <span class="s s4">429</span></p></section><section id="putAnnounce"><h3><span class="v put">PUT</span> <code>/{scene}/{stream}/announce/v1/{key}</code></h3><p class="sum">Publish or replace a spatial.disco.Announce <span class="auth">token</span></p><p>**Writes are per lane.** The path&#x27;s first segment is the scene; the bearer token must be granted it (<code>tenancy.WRITE_REFUSALS</code>). Every member of the record is required: a body is a whole record, never a patch. The body&#x27;s key must equal the URL&#x27;s. <code>If-Match</code> is honoured when present.</p><p class="codes"><span class="s s2">200</span> <span class="s s2">201</span> <span class="s s4">400</span> <span class="s s4">401</span> <span class="s s4">403</span> <span class="s s4">405</span> <span class="s s4">412</span> <span class="s s4">429</span> <span class="s s4">413</span> <span class="s s4">415</span> <span class="s s5">503</span></p></section><section id="withdrawAnnounce"><h3><span class="v delete">DELETE</span> <code>/{scene}/{stream}/announce/v1/{key}</code></h3><p class="sum">Withdraw (dispose) a spatial.disco.Announce <span class="auth">token</span></p><p>**Writes are per lane.** The path&#x27;s first segment is the scene; the bearer token must be granted it (<code>tenancy.WRITE_REFUSALS</code>). The dispose streams on the collection&#x27;s WebSocket with its <code>reason</code>.</p><p class="codes"><span class="s s2">204</span> <span class="s s4">400</span> <span class="s s4">401</span> <span class="s s4">403</span> <span class="s s4">404</span> <span class="s s4">405</span> <span class="s s4">429</span> <span class="s s5">503</span></p></section><section id="listContentAnnounce"><h3><span class="v get">GET</span> <code>/{scene}/{stream}/content_announce/v1</code></h3><p class="sum">Every spatial.disco.ContentAnnounce instance in the latched set <span class="auth">open</span></p><p>**The same URL is also a WebSocket** (N.5): connect with <code>wss://</code> and receive the latched set as <code>sample</code> events, exactly one <code>end_of_replay</code> carrying the replay count, then live <code>sample</code> and <code>dispose</code> events (<code>{event, topic, key, data | reason}</code>). Not modelled here; see [Appendix N.5](https://spatialdds.org/v1.8/appendix-n/).</p><p class="codes"><span class="s s2">200</span> <span class="s s3">304</span> <span class="s s4">404</span> <span class="s s4">429</span> <span class="s s5">503</span></p></section><section id="commandContentAnnounce"><h3><span class="v post">POST</span> <code>/{scene}/{stream}/content_announce/v1</code></h3><p class="sum">An N.6 command addressed through the content_announce lane <span class="auth">token</span></p><p>Accepted only on a lane whose topic advertises <code>accepts_commands</code>; otherwise <code>405</code>. **Writes are per lane.** The path&#x27;s first segment is the scene; the bearer token must be granted it (<code>tenancy.WRITE_REFUSALS</code>). Every member of the record is required: a body is a whole record, never a patch.</p><p class="codes"><span class="s s2">202</span> <span class="s s4">400</span> <span class="s s4">401</span> <span class="s s4">403</span> <span class="s s4">405</span> <span class="s s4">409</span> <span class="s s4">429</span> <span class="s s4">413</span> <span class="s s4">415</span></p></section><section id="getContentAnnounce"><h3><span class="v get">GET</span> <code>/{scene}/{stream}/content_announce/v1/{key}</code></h3><p class="sum">One spatial.disco.ContentAnnounce instance <span class="auth">open</span></p><p class="codes"><span class="s s2">200</span> <span class="s s3">304</span> <span class="s s4">404</span> <span class="s s4">429</span></p></section><section id="putContentAnnounce"><h3><span class="v put">PUT</span> <code>/{scene}/{stream}/content_announce/v1/{key}</code></h3><p class="sum">Publish or replace a spatial.disco.ContentAnnounce <span class="auth">token</span></p><p>**Writes are per lane.** The path&#x27;s first segment is the scene; the bearer token must be granted it (<code>tenancy.WRITE_REFUSALS</code>). Every member of the record is required: a body is a whole record, never a patch. The body&#x27;s key must equal the URL&#x27;s. <code>If-Match</code> is honoured when present.</p><p class="codes"><span class="s s2">200</span> <span class="s s2">201</span> <span class="s s4">400</span> <span class="s s4">401</span> <span class="s s4">403</span> <span class="s s4">405</span> <span class="s s4">412</span> <span class="s s4">429</span> <span class="s s4">413</span> <span class="s s4">415</span> <span class="s s5">503</span></p></section><section id="withdrawContentAnnounce"><h3><span class="v delete">DELETE</span> <code>/{scene}/{stream}/content_announce/v1/{key}</code></h3><p class="sum">Withdraw (dispose) a spatial.disco.ContentAnnounce <span class="auth">token</span></p><p>**Writes are per lane.** The path&#x27;s first segment is the scene; the bearer token must be granted it (<code>tenancy.WRITE_REFUSALS</code>). The dispose streams on the collection&#x27;s WebSocket with its <code>reason</code>.</p><p class="codes"><span class="s s2">204</span> <span class="s s4">400</span> <span class="s s4">401</span> <span class="s s4">403</span> <span class="s s4">404</span> <span class="s s4">405</span> <span class="s s4">429</span> <span class="s s5">503</span></p></section><section id="listGeoAnchor"><h3><span class="v get">GET</span> <code>/{scene}/{stream}/geo_anchor/v1</code></h3><p class="sum">Every spatial.core.GeoAnchor instance in the latched set <span class="auth">open</span></p><p>**The same URL is also a WebSocket** (N.5): connect with <code>wss://</code> and receive the latched set as <code>sample</code> events, exactly one <code>end_of_replay</code> carrying the replay count, then live <code>sample</code> and <code>dispose</code> events (<code>{event, topic, key, data | reason}</code>). Not modelled here; see [Appendix N.5](https://spatialdds.org/v1.8/appendix-n/).</p><p class="codes"><span class="s s2">200</span> <span class="s s3">304</span> <span class="s s4">404</span> <span class="s s4">429</span> <span class="s s5">503</span></p></section><section id="commandGeoAnchor"><h3><span class="v post">POST</span> <code>/{scene}/{stream}/geo_anchor/v1</code></h3><p class="sum">An N.6 command addressed through the geo_anchor lane <span class="auth">token</span></p><p>Accepted only on a lane whose topic advertises <code>accepts_commands</code>; otherwise <code>405</code>. **Writes are per lane.** The path&#x27;s first segment is the scene; the bearer token must be granted it (<code>tenancy.WRITE_REFUSALS</code>). Every member of the record is required: a body is a whole record, never a patch.</p><p class="codes"><span class="s s2">202</span> <span class="s s4">400</span> <span class="s s4">401</span> <span class="s s4">403</span> <span class="s s4">405</span> <span class="s s4">409</span> <span class="s s4">429</span> <span class="s s4">413</span> <span class="s s4">415</span></p></section><section id="getGeoAnchor"><h3><span class="v get">GET</span> <code>/{scene}/{stream}/geo_anchor/v1/{key}</code></h3><p class="sum">One spatial.core.GeoAnchor instance <span class="auth">open</span></p><p class="codes"><span class="s s2">200</span> <span class="s s3">304</span> <span class="s s4">404</span> <span class="s s4">429</span></p></section><section id="putGeoAnchor"><h3><span class="v put">PUT</span> <code>/{scene}/{stream}/geo_anchor/v1/{key}</code></h3><p class="sum">Publish or replace a spatial.core.GeoAnchor <span class="auth">token</span></p><p>**Writes are per lane.** The path&#x27;s first segment is the scene; the bearer token must be granted it (<code>tenancy.WRITE_REFUSALS</code>). Every member of the record is required: a body is a whole record, never a patch. The body&#x27;s key must equal the URL&#x27;s. <code>If-Match</code> is honoured when present.</p><p class="codes"><span class="s s2">200</span> <span class="s s2">201</span> <span class="s s4">400</span> <span class="s s4">401</span> <span class="s s4">403</span> <span class="s s4">405</span> <span class="s s4">412</span> <span class="s s4">429</span> <span class="s s4">413</span> <span class="s s4">415</span> <span class="s s5">503</span></p></section><section id="withdrawGeoAnchor"><h3><span class="v delete">DELETE</span> <code>/{scene}/{stream}/geo_anchor/v1/{key}</code></h3><p class="sum">Withdraw (dispose) a spatial.core.GeoAnchor <span class="auth">token</span></p><p>**Writes are per lane.** The path&#x27;s first segment is the scene; the bearer token must be granted it (<code>tenancy.WRITE_REFUSALS</code>). The dispose streams on the collection&#x27;s WebSocket with its <code>reason</code>.</p><p class="codes"><span class="s s2">204</span> <span class="s s4">400</span> <span class="s s4">401</span> <span class="s s4">403</span> <span class="s s4">404</span> <span class="s s4">405</span> <span class="s s4">429</span> <span class="s s5">503</span></p></section><section id="listInstances"><h3><span class="v get">GET</span> <code>/{scene}/{stream}/{type}/{version}</code></h3><p class="sum">Any other served topic, read-only <span class="auth">token for operator-scoped</span></p><p>Latched types answer with the latched set; streamed types with the replayed window. Public or operator-scoped per the type: <code>spatial.core.FrameTransform</code> latched public, <code>spatial.events.CrossingLine</code> latched public, <code>spatial.events.SpatialEvent</code> stream bearer, <code>spatial.events.SpatialZone</code> latched public, <code>spatial.owm.Entity</code> latched bearer, <code>spatial.owm.ModelPose</code> stream bearer, <code>spatial.semantics.Detection2DSet</code> stream bearer, <code>spatial.semantics.Detection3DSet</code> stream bearer, <code>spatial.semantics.FusedTrackSet</code> stream bearer. **The same URL is also a WebSocket** (N.5): connect with <code>wss://</code> and receive the latched set as <code>sample</code> events, exactly one <code>end_of_replay</code> carrying the replay count, then live <code>sample</code> and <code>dispose</code> events (<code>{event, topic, key, data | reason}</code>). Not modelled here; see [Appendix N.5](https://spatialdds.org/v1.8/appendix-n/).</p><p class="codes"><span class="s s2">200</span> <span class="s s3">304</span> <span class="s s4">401</span> <span class="s s4">404</span> <span class="s s4">429</span> <span class="s s5">503</span></p></section><section id="listWellKnownInstances"><h3><span class="v get">GET</span> <code>/.well-known/spatialdds/{stream}/{type}/{version}</code></h3><p class="sum">A four-segment topic&#x27;s collection <span class="auth">token for operator-scoped</span></p><p>A four-segment topic (<code>spatialdds/&lt;stream&gt;/&lt;type&gt;/&lt;version&gt;</code>, no scene), such as the discovery announce lane, served exactly as a scene-scoped collection is: latched set, <code>ETag</code>, <code>304</code>, the same access classes, and the N.5 stream by upgrading the same path. Read-only: a topic with no scene is in no lane. **The same URL is also a WebSocket** (N.5): connect with <code>wss://</code> and receive the latched set as <code>sample</code> events, exactly one <code>end_of_replay</code> carrying the replay count, then live <code>sample</code> and <code>dispose</code> events (<code>{event, topic, key, data | reason}</code>). Not modelled here; see [Appendix N.5](https://spatialdds.org/v1.8/appendix-n/).</p><p class="codes"><span class="s s2">200</span> <span class="s s3">304</span> <span class="s s4">401</span> <span class="s s4">404</span> <span class="s s4">429</span> <span class="s s5">503</span></p></section><section id="getWellKnownInstance"><h3><span class="v get">GET</span> <code>/.well-known/spatialdds/{stream}/{type}/{version}/{key}</code></h3><p class="sum">One instance of a four-segment topic <span class="auth">token for operator-scoped</span></p><p>A four-segment topic (<code>spatialdds/&lt;stream&gt;/&lt;type&gt;/&lt;version&gt;</code>, no scene), such as the discovery announce lane, served exactly as a scene-scoped collection is: latched set, <code>ETag</code>, <code>304</code>, the same access classes, and the N.5 stream by upgrading the same path. Read-only: a topic with no scene is in no lane. </p><p class="codes"><span class="s s2">200</span> <span class="s s3">304</span> <span class="s s4">401</span> <span class="s s4">404</span> <span class="s s4">429</span> <span class="s s5">503</span></p></section><h2>Schemas</h2><p class=mut>115 components, from the published JSON Schemas at <code>/.well-known/spatialdds/schemas</code>.</p></main></body></html>
17
+ .s{margin-right:.5rem}.s4,.s5{color:#b5482c}a{color:var(--acc)}</style></head><body><main><h1>SpatialDDS web binding (spatialdds-py)</h1><p class=mut>Version 0.7.4 · OpenAPI 3.1.0 · generated, never hand-written · <a href='openapi'>openapi.yaml</a></p><p>Generated by <code>tools/gen_openapi.py</code> from the gateway&#x27;s own tables and the published JSON Schemas; never edited by hand. Reads are open; writes take a bearer token scoped to scenes. The WebSocket streams (N.5) are described on each collection path, not modelled.</p><h2>Operations</h2><section id="search"><h3><span class="v get">GET</span> <code>/.well-known/spatialdds/search</code></h3><p class="sum">Services near a geohash (§8.2.3 manifests) <span class="auth">open</span></p><p class="codes"><span class="s s2">200</span> <span class="s s4">429</span></p></section><section id="searchPost"><h3><span class="v post">POST</span> <code>/.well-known/spatialdds/search</code></h3><p class="sum">The same search, as a POST body <span class="auth">open</span></p><p class="codes"><span class="s s2">200</span> <span class="s s4">429</span> <span class="s s4">413</span> <span class="s s4">415</span></p></section><section id="listSchemas"><h3><span class="v get">GET</span> <code>/.well-known/spatialdds/schemas</code></h3><p class="sum">The published JSON Schemas, by type <span class="auth">open</span></p><p class="codes"><span class="s s2">200</span> <span class="s s4">429</span></p></section><section id="getSchema"><h3><span class="v get">GET</span> <code>/.well-known/spatialdds/schemas/{type}</code></h3><p class="sum"> <span class="auth">open</span></p><p class="codes"><span class="s s2">200</span> <span class="s s4">404</span> <span class="s s4">429</span></p></section><section id="bootstrap"><h3><span class="v get">GET</span> <code>/.well-known/spatialdds/bootstrap</code></h3><p class="sum">DDS-join material (N.2); optional <span class="auth">open</span></p><p class="codes"><span class="s s2">200</span> <span class="s s4">404</span> <span class="s s4">429</span></p></section><section id="resolverMetadata"><h3><span class="v get">GET</span> <code>/.well-known/spatialdds/resolver</code></h3><p class="sum">§7.5.2 resolver metadata <span class="auth">open</span></p><p class="codes"><span class="s s2">200</span> <span class="s s4">404</span> <span class="s s4">429</span></p></section><section id="openapi"><h3><span class="v get">GET</span> <code>/.well-known/spatialdds/openapi</code></h3><p class="sum">This document <span class="auth">open</span></p><p class="codes"><span class="s s2">200</span> <span class="s s4">429</span></p></section><section id="openapiPage"><h3><span class="v get">GET</span> <code>/.well-known/spatialdds/openapi.html</code></h3><p class="sum">This document as a static reference page <span class="auth">open</span></p><p class="codes"><span class="s s2">200</span> <span class="s s4">429</span></p></section><section id="resolve"><h3><span class="v get">GET</span> <code>/spatialdds/resolve</code></h3><p class="sum">Resolve a URI to its manifest (§7.5.2) <span class="auth">open</span></p><p class="codes"><span class="s s2">200</span> <span class="s s4">400</span> <span class="s s4">404</span> <span class="s s4">429</span></p></section><section id="putManifest"><h3><span class="v put">PUT</span> <code>/spatialdds/resolve</code></h3><p class="sum">Publish a manifest, keyed by its own <code>id</code> <span class="auth">token</span></p><p>The <code>uri</code> parameter is not used; the body&#x27;s <code>id</code> is the key. Its scene segment is the lane. **Writes are per lane.** The path&#x27;s first segment is the scene; the bearer token must be granted it (<code>tenancy.WRITE_REFUSALS</code>).</p><p class="codes"><span class="s s2">200</span> <span class="s s2">201</span> <span class="s s4">400</span> <span class="s s4">401</span> <span class="s s4">403</span> <span class="s s4">405</span> <span class="s s4">429</span> <span class="s s4">413</span> <span class="s s4">415</span> <span class="s s5">503</span></p></section><section id="withdrawManifest"><h3><span class="v delete">DELETE</span> <code>/spatialdds/resolve</code></h3><p class="sum">Withdraw a manifest; <code>resolve</code> then answers 404 <span class="auth">token</span></p><p>Owner-checked by the URI&#x27;s scene segment. **Writes are per lane.** The path&#x27;s first segment is the scene; the bearer token must be granted it (<code>tenancy.WRITE_REFUSALS</code>).</p><p class="codes"><span class="s s2">204</span> <span class="s s4">400</span> <span class="s s4">401</span> <span class="s s4">403</span> <span class="s s4">404</span> <span class="s s4">405</span> <span class="s s4">429</span> <span class="s s5">503</span></p></section><section id="getAsset"><h3><span class="v get">GET</span> <code>/assets/{name}</code></h3><p class="sum">Asset bytes this endpoint hosts (implementation-defined) <span class="auth">open</span></p><p class="codes"><span class="s s2">200</span> <span class="s s4">404</span> <span class="s s4">429</span></p></section><section id="listAnnounce"><h3><span class="v get">GET</span> <code>/{scene}/{stream}/announce/v1</code></h3><p class="sum">Every spatial.disco.Announce instance in the latched set <span class="auth">open</span></p><p>**The same URL is also a WebSocket** (N.5): connect with <code>wss://</code> and receive the latched set as <code>sample</code> events, exactly one <code>end_of_replay</code> carrying the replay count, then live <code>sample</code> and <code>dispose</code> events (<code>{event, topic, key, data | reason}</code>). Not modelled here; see [Appendix N.5](https://spatialdds.org/v1.8/appendix-n/).</p><p class="codes"><span class="s s2">200</span> <span class="s s3">304</span> <span class="s s4">404</span> <span class="s s4">429</span> <span class="s s5">503</span></p></section><section id="commandAnnounce"><h3><span class="v post">POST</span> <code>/{scene}/{stream}/announce/v1</code></h3><p class="sum">An N.6 command addressed through the announce lane <span class="auth">token</span></p><p>Accepted only on a lane whose topic advertises <code>accepts_commands</code>; otherwise <code>405</code>. **Writes are per lane.** The path&#x27;s first segment is the scene; the bearer token must be granted it (<code>tenancy.WRITE_REFUSALS</code>). Every member of the record is required: a body is a whole record, never a patch.</p><p class="codes"><span class="s s2">202</span> <span class="s s4">400</span> <span class="s s4">401</span> <span class="s s4">403</span> <span class="s s4">405</span> <span class="s s4">409</span> <span class="s s4">429</span> <span class="s s4">413</span> <span class="s s4">415</span></p></section><section id="getAnnounce"><h3><span class="v get">GET</span> <code>/{scene}/{stream}/announce/v1/{key}</code></h3><p class="sum">One spatial.disco.Announce instance <span class="auth">open</span></p><p class="codes"><span class="s s2">200</span> <span class="s s3">304</span> <span class="s s4">404</span> <span class="s s4">429</span></p></section><section id="putAnnounce"><h3><span class="v put">PUT</span> <code>/{scene}/{stream}/announce/v1/{key}</code></h3><p class="sum">Publish or replace a spatial.disco.Announce <span class="auth">token</span></p><p>**Writes are per lane.** The path&#x27;s first segment is the scene; the bearer token must be granted it (<code>tenancy.WRITE_REFUSALS</code>). Every member of the record is required: a body is a whole record, never a patch. The body&#x27;s key must equal the URL&#x27;s. <code>If-Match</code> is honoured when present.</p><p class="codes"><span class="s s2">200</span> <span class="s s2">201</span> <span class="s s4">400</span> <span class="s s4">401</span> <span class="s s4">403</span> <span class="s s4">405</span> <span class="s s4">412</span> <span class="s s4">429</span> <span class="s s4">413</span> <span class="s s4">415</span> <span class="s s5">503</span></p></section><section id="withdrawAnnounce"><h3><span class="v delete">DELETE</span> <code>/{scene}/{stream}/announce/v1/{key}</code></h3><p class="sum">Withdraw (dispose) a spatial.disco.Announce <span class="auth">token</span></p><p>**Writes are per lane.** The path&#x27;s first segment is the scene; the bearer token must be granted it (<code>tenancy.WRITE_REFUSALS</code>). The dispose streams on the collection&#x27;s WebSocket with its <code>reason</code>.</p><p class="codes"><span class="s s2">204</span> <span class="s s4">400</span> <span class="s s4">401</span> <span class="s s4">403</span> <span class="s s4">404</span> <span class="s s4">405</span> <span class="s s4">429</span> <span class="s s5">503</span></p></section><section id="listContentAnnounce"><h3><span class="v get">GET</span> <code>/{scene}/{stream}/content_announce/v1</code></h3><p class="sum">Every spatial.disco.ContentAnnounce instance in the latched set <span class="auth">open</span></p><p>**The same URL is also a WebSocket** (N.5): connect with <code>wss://</code> and receive the latched set as <code>sample</code> events, exactly one <code>end_of_replay</code> carrying the replay count, then live <code>sample</code> and <code>dispose</code> events (<code>{event, topic, key, data | reason}</code>). Not modelled here; see [Appendix N.5](https://spatialdds.org/v1.8/appendix-n/).</p><p class="codes"><span class="s s2">200</span> <span class="s s3">304</span> <span class="s s4">404</span> <span class="s s4">429</span> <span class="s s5">503</span></p></section><section id="commandContentAnnounce"><h3><span class="v post">POST</span> <code>/{scene}/{stream}/content_announce/v1</code></h3><p class="sum">An N.6 command addressed through the content_announce lane <span class="auth">token</span></p><p>Accepted only on a lane whose topic advertises <code>accepts_commands</code>; otherwise <code>405</code>. **Writes are per lane.** The path&#x27;s first segment is the scene; the bearer token must be granted it (<code>tenancy.WRITE_REFUSALS</code>). Every member of the record is required: a body is a whole record, never a patch.</p><p class="codes"><span class="s s2">202</span> <span class="s s4">400</span> <span class="s s4">401</span> <span class="s s4">403</span> <span class="s s4">405</span> <span class="s s4">409</span> <span class="s s4">429</span> <span class="s s4">413</span> <span class="s s4">415</span></p></section><section id="getContentAnnounce"><h3><span class="v get">GET</span> <code>/{scene}/{stream}/content_announce/v1/{key}</code></h3><p class="sum">One spatial.disco.ContentAnnounce instance <span class="auth">open</span></p><p class="codes"><span class="s s2">200</span> <span class="s s3">304</span> <span class="s s4">404</span> <span class="s s4">429</span></p></section><section id="putContentAnnounce"><h3><span class="v put">PUT</span> <code>/{scene}/{stream}/content_announce/v1/{key}</code></h3><p class="sum">Publish or replace a spatial.disco.ContentAnnounce <span class="auth">token</span></p><p>**Writes are per lane.** The path&#x27;s first segment is the scene; the bearer token must be granted it (<code>tenancy.WRITE_REFUSALS</code>). Every member of the record is required: a body is a whole record, never a patch. The body&#x27;s key must equal the URL&#x27;s. <code>If-Match</code> is honoured when present.</p><p class="codes"><span class="s s2">200</span> <span class="s s2">201</span> <span class="s s4">400</span> <span class="s s4">401</span> <span class="s s4">403</span> <span class="s s4">405</span> <span class="s s4">412</span> <span class="s s4">429</span> <span class="s s4">413</span> <span class="s s4">415</span> <span class="s s5">503</span></p></section><section id="withdrawContentAnnounce"><h3><span class="v delete">DELETE</span> <code>/{scene}/{stream}/content_announce/v1/{key}</code></h3><p class="sum">Withdraw (dispose) a spatial.disco.ContentAnnounce <span class="auth">token</span></p><p>**Writes are per lane.** The path&#x27;s first segment is the scene; the bearer token must be granted it (<code>tenancy.WRITE_REFUSALS</code>). The dispose streams on the collection&#x27;s WebSocket with its <code>reason</code>.</p><p class="codes"><span class="s s2">204</span> <span class="s s4">400</span> <span class="s s4">401</span> <span class="s s4">403</span> <span class="s s4">404</span> <span class="s s4">405</span> <span class="s s4">429</span> <span class="s s5">503</span></p></section><section id="listGeoAnchor"><h3><span class="v get">GET</span> <code>/{scene}/{stream}/geo_anchor/v1</code></h3><p class="sum">Every spatial.core.GeoAnchor instance in the latched set <span class="auth">open</span></p><p>**The same URL is also a WebSocket** (N.5): connect with <code>wss://</code> and receive the latched set as <code>sample</code> events, exactly one <code>end_of_replay</code> carrying the replay count, then live <code>sample</code> and <code>dispose</code> events (<code>{event, topic, key, data | reason}</code>). Not modelled here; see [Appendix N.5](https://spatialdds.org/v1.8/appendix-n/).</p><p class="codes"><span class="s s2">200</span> <span class="s s3">304</span> <span class="s s4">404</span> <span class="s s4">429</span> <span class="s s5">503</span></p></section><section id="commandGeoAnchor"><h3><span class="v post">POST</span> <code>/{scene}/{stream}/geo_anchor/v1</code></h3><p class="sum">An N.6 command addressed through the geo_anchor lane <span class="auth">token</span></p><p>Accepted only on a lane whose topic advertises <code>accepts_commands</code>; otherwise <code>405</code>. **Writes are per lane.** The path&#x27;s first segment is the scene; the bearer token must be granted it (<code>tenancy.WRITE_REFUSALS</code>). Every member of the record is required: a body is a whole record, never a patch.</p><p class="codes"><span class="s s2">202</span> <span class="s s4">400</span> <span class="s s4">401</span> <span class="s s4">403</span> <span class="s s4">405</span> <span class="s s4">409</span> <span class="s s4">429</span> <span class="s s4">413</span> <span class="s s4">415</span></p></section><section id="getGeoAnchor"><h3><span class="v get">GET</span> <code>/{scene}/{stream}/geo_anchor/v1/{key}</code></h3><p class="sum">One spatial.core.GeoAnchor instance <span class="auth">open</span></p><p class="codes"><span class="s s2">200</span> <span class="s s3">304</span> <span class="s s4">404</span> <span class="s s4">429</span></p></section><section id="putGeoAnchor"><h3><span class="v put">PUT</span> <code>/{scene}/{stream}/geo_anchor/v1/{key}</code></h3><p class="sum">Publish or replace a spatial.core.GeoAnchor <span class="auth">token</span></p><p>**Writes are per lane.** The path&#x27;s first segment is the scene; the bearer token must be granted it (<code>tenancy.WRITE_REFUSALS</code>). Every member of the record is required: a body is a whole record, never a patch. The body&#x27;s key must equal the URL&#x27;s. <code>If-Match</code> is honoured when present.</p><p class="codes"><span class="s s2">200</span> <span class="s s2">201</span> <span class="s s4">400</span> <span class="s s4">401</span> <span class="s s4">403</span> <span class="s s4">405</span> <span class="s s4">412</span> <span class="s s4">429</span> <span class="s s4">413</span> <span class="s s4">415</span> <span class="s s5">503</span></p></section><section id="withdrawGeoAnchor"><h3><span class="v delete">DELETE</span> <code>/{scene}/{stream}/geo_anchor/v1/{key}</code></h3><p class="sum">Withdraw (dispose) a spatial.core.GeoAnchor <span class="auth">token</span></p><p>**Writes are per lane.** The path&#x27;s first segment is the scene; the bearer token must be granted it (<code>tenancy.WRITE_REFUSALS</code>). The dispose streams on the collection&#x27;s WebSocket with its <code>reason</code>.</p><p class="codes"><span class="s s2">204</span> <span class="s s4">400</span> <span class="s s4">401</span> <span class="s s4">403</span> <span class="s s4">404</span> <span class="s s4">405</span> <span class="s s4">429</span> <span class="s s5">503</span></p></section><section id="listInstances"><h3><span class="v get">GET</span> <code>/{scene}/{stream}/{type}/{version}</code></h3><p class="sum">Any other served topic, read-only <span class="auth">token for operator-scoped</span></p><p>Latched types answer with the latched set; streamed types with the replayed window. Public or operator-scoped per the type: <code>spatial.core.FrameTransform</code> latched public, <code>spatial.events.CrossingLine</code> latched public, <code>spatial.events.SpatialEvent</code> stream bearer, <code>spatial.events.SpatialZone</code> latched public, <code>spatial.owm.Entity</code> latched bearer, <code>spatial.owm.ModelPose</code> stream bearer, <code>spatial.semantics.Detection2DSet</code> stream bearer, <code>spatial.semantics.Detection3DSet</code> stream bearer, <code>spatial.semantics.FusedTrackSet</code> stream bearer. **The same URL is also a WebSocket** (N.5): connect with <code>wss://</code> and receive the latched set as <code>sample</code> events, exactly one <code>end_of_replay</code> carrying the replay count, then live <code>sample</code> and <code>dispose</code> events (<code>{event, topic, key, data | reason}</code>). Not modelled here; see [Appendix N.5](https://spatialdds.org/v1.8/appendix-n/).</p><p class="codes"><span class="s s2">200</span> <span class="s s3">304</span> <span class="s s4">401</span> <span class="s s4">404</span> <span class="s s4">429</span> <span class="s s5">503</span></p></section><section id="listWellKnownInstances"><h3><span class="v get">GET</span> <code>/.well-known/spatialdds/{stream}/{type}/{version}</code></h3><p class="sum">A four-segment topic&#x27;s collection <span class="auth">token for operator-scoped</span></p><p>A four-segment topic (<code>spatialdds/&lt;stream&gt;/&lt;type&gt;/&lt;version&gt;</code>, no scene), such as the discovery announce lane, served exactly as a scene-scoped collection is: latched set, <code>ETag</code>, <code>304</code>, the same access classes, and the N.5 stream by upgrading the same path. Read-only: a topic with no scene is in no lane. **The same URL is also a WebSocket** (N.5): connect with <code>wss://</code> and receive the latched set as <code>sample</code> events, exactly one <code>end_of_replay</code> carrying the replay count, then live <code>sample</code> and <code>dispose</code> events (<code>{event, topic, key, data | reason}</code>). Not modelled here; see [Appendix N.5](https://spatialdds.org/v1.8/appendix-n/).</p><p class="codes"><span class="s s2">200</span> <span class="s s3">304</span> <span class="s s4">401</span> <span class="s s4">404</span> <span class="s s4">429</span> <span class="s s5">503</span></p></section><section id="getWellKnownInstance"><h3><span class="v get">GET</span> <code>/.well-known/spatialdds/{stream}/{type}/{version}/{key}</code></h3><p class="sum">One instance of a four-segment topic <span class="auth">token for operator-scoped</span></p><p>A four-segment topic (<code>spatialdds/&lt;stream&gt;/&lt;type&gt;/&lt;version&gt;</code>, no scene), such as the discovery announce lane, served exactly as a scene-scoped collection is: latched set, <code>ETag</code>, <code>304</code>, the same access classes, and the N.5 stream by upgrading the same path. Read-only: a topic with no scene is in no lane. </p><p class="codes"><span class="s s2">200</span> <span class="s s3">304</span> <span class="s s4">401</span> <span class="s s4">404</span> <span class="s s4">429</span> <span class="s s5">503</span></p></section><h2>Schemas</h2><p class=mut>115 components, from the published JSON Schemas at <code>/.well-known/spatialdds/schemas</code>.</p></main></body></html>
@@ -2,7 +2,7 @@
2
2
  openapi: 3.1.0
3
3
  info:
4
4
  title: SpatialDDS web binding (spatialdds-py)
5
- version: 0.7.3
5
+ version: 0.7.4
6
6
  summary: SpatialDDS 1.8 Appendix N over HTTPS, plus this endpoint's write surface.
7
7
  description: Generated by `tools/gen_openapi.py` from the gateway's own tables and the published JSON Schemas; never edited by hand. Reads are open; writes take a bearer token scoped to scenes. The WebSocket streams (N.5) are described on each collection path, not modelled.
8
8
  servers:
@@ -30,7 +30,7 @@ __all__ = [
30
30
  "ON_EXIT", "DOMAIN_ENV",
31
31
  "to_json", "from_json", "validate", "SchemaInvalid", "WireError",
32
32
  "fqn_of", "key_of",
33
- "class_for", "extend", "Extension",
33
+ "class_for", "extend", "Extension", "extensions", "extension_of",
34
34
  "registry", "Profile", "RegistryError", "topic_name",
35
35
  "ANNOUNCE", "DEPART", "CONTENT", "QUERY", "COVERAGE_HINT",
36
36
  "Recorder", "chunk", "Reassembler", "CorruptChunk", "blob_ref",
@@ -53,6 +53,7 @@ _FROM = {
53
53
  "to_json": "codec", "from_json": "codec", "validate": "codec",
54
54
  "SchemaInvalid": "codec", "WireError": "codec", "fqn_of": "codec", "key_of": "codec",
55
55
  "class_for": "codec", "extend": "codec", "Extension": "codec",
56
+ "extensions": "codec", "extension_of": "codec",
56
57
  "Profile": "registry", "RegistryError": "registry",
57
58
  "topic_name": "registry",
58
59
  "Recorder": "record",
@@ -109,6 +109,28 @@ def _now_time():
109
109
  return Time(sec=int(t), nanosec=int((t % 1) * 1e9))
110
110
 
111
111
 
112
+ def _wait_for_acks(dw: Any, timeout_s: float, what: str) -> bool:
113
+ """`DataWriter.wait_for_acks`, False on timeout, as it is documented.
114
+
115
+ cyclonedds-python 11.0.1's timeout branch reads
116
+ `Exception.DDS_RETCODE_TIMEOUT`, which does not exist (`cyclonedds/pub.py`
117
+ line 325), so every timeout raises AttributeError. `close()` awaits acks
118
+ after writing each dispose and Depart, so one slow peer made a clean stop
119
+ exit with a traceback and skip the remaining flushes and every recorder's
120
+ `flush()` (S17). That AttributeError, and only that one, is the timeout.
121
+ """
122
+ try:
123
+ acked = bool(dw.wait_for_acks(duration(seconds=timeout_s)))
124
+ except AttributeError as error:
125
+ if "DDS_RETCODE_TIMEOUT" not in str(error):
126
+ raise
127
+ acked = False
128
+ if not acked:
129
+ log.warning("%s: not every reader acknowledged within %.1f s",
130
+ what, timeout_s)
131
+ return acked
132
+
133
+
112
134
  def _has_member(sample: Any, name: str) -> bool:
113
135
  return any(f.name == name for f in dataclasses.fields(sample))
114
136
 
@@ -275,7 +297,8 @@ class Writer:
275
297
  with self._lock:
276
298
  self._dw.write(sample)
277
299
  self._last[key] = sample
278
- self._p._recorded(self.topic, self._lane.fqn, sample)
300
+ self._p._recorded(self.topic, self._lane.fqn, sample,
301
+ self._lane.profile.durability)
279
302
  return sample
280
303
 
281
304
  def _terminal(self, sample: Any, reason: str) -> Any:
@@ -298,27 +321,51 @@ class Writer:
298
321
  changes["stamp"] = _now_time()
299
322
  return dataclasses.replace(sample, **changes) if changes else sample
300
323
 
301
- def withdraw(self, key: str, *, reason: str) -> None:
324
+ def withdraw(self, key: str, *, reason: str, **changes: Any) -> None:
302
325
  """Remove an instance: its terminal sample, then the dispose (§2.14, N.5.2).
303
326
 
304
327
  `reason` is required and may not be empty. It is carried wherever a
305
328
  carrier exists: the type's own member when it has one, the recording's
306
329
  dispose metadata always, and this layer's log always.
330
+
331
+ `changes` are the type's other members as the terminal sample should
332
+ carry them, such as a validity interval closed at retirement
333
+ (`has_valid_until=True, valid_until=...`). They are applied to the
334
+ last sample before the terminal members, so the instance ends in one
335
+ terminal sample. Until 0.7.4 a type with such members had to write
336
+ its terminal state itself, and this wrote it again (S18). The members
337
+ this method sets (`state`, `state_reason`, `stamp`) and the key are
338
+ not changes a caller can make.
307
339
  """
308
340
  if not isinstance(reason, str) or not reason.strip():
309
341
  raise ValueError("withdraw() needs a non-empty reason=: a removal "
310
342
  "is a claim, and §2.14 makes it say why")
343
+ fixed = {"state", "state_reason", "stamp", codec.key_member(self._lane.fqn)}
344
+ refused = sorted(set(changes) & fixed)
345
+ if refused:
346
+ raise ValueError(f"withdraw() sets {', '.join(refused)} itself; "
347
+ f"pass reason=, and the other members to change")
311
348
  with self._lock:
312
349
  if key not in self._last:
313
350
  raise KeyError(f"{self.topic} holds no instance keyed {key!r} "
314
351
  f"from this writer (held: "
315
352
  f"{', '.join(sorted(self._last)) or 'none'})")
316
- terminal = self._terminal(self._last.pop(key), reason)
353
+ last = self._last[key]
354
+ unknown = sorted(n for n in changes if not _has_member(last, n))
355
+ if unknown:
356
+ raise ValueError(f"{self._lane.fqn} has no member "
357
+ f"{', '.join(unknown)}")
358
+ terminal = self._terminal(dataclasses.replace(last, **changes),
359
+ reason)
360
+ if changes and self.validate:
361
+ codec.validate(terminal)
362
+ del self._last[key]
317
363
  # Immediately: nothing of this writer's may come between the
318
364
  # terminal sample and its dispose (N.5.2).
319
365
  self._dw.write(terminal)
320
366
  self._dw.dispose(terminal)
321
- self._p._recorded(self.topic, self._lane.fqn, terminal)
367
+ self._p._recorded(self.topic, self._lane.fqn, terminal,
368
+ self._lane.profile.durability)
322
369
  self._p._recorded_dispose(self.topic, key, reason)
323
370
  log.info("withdrew %s from %s: %s", key, self.topic, reason)
324
371
 
@@ -353,7 +400,7 @@ class Writer:
353
400
  self._dw.unregister_instance(sample)
354
401
 
355
402
  def _flush(self, timeout_s: float) -> bool:
356
- return bool(self._dw.wait_for_acks(duration(seconds=timeout_s)))
403
+ return _wait_for_acks(self._dw, timeout_s, self.topic)
357
404
 
358
405
  def topic_meta(self) -> Dict[str, Any]:
359
406
  """This lane as one `TopicMeta`, through the web layer's builder."""
@@ -1008,9 +1055,10 @@ class Participant:
1008
1055
  svc["last"])
1009
1056
  return r
1010
1057
 
1011
- def _recorded(self, topic: str, fqn: str, sample: Any) -> None:
1058
+ def _recorded(self, topic: str, fqn: str, sample: Any,
1059
+ durability: str = "") -> None:
1012
1060
  for r in self._recorders:
1013
- r.sample(topic, fqn, sample)
1061
+ r.sample(topic, fqn, sample, durability)
1014
1062
 
1015
1063
  def _recorded_dispose(self, topic: str, key: str, reason: str) -> None:
1016
1064
  for r in self._recorders:
@@ -1039,7 +1087,7 @@ class Participant:
1039
1087
  for w in self._writers:
1040
1088
  w._flush(flush_s)
1041
1089
  for dw in self._presence_writers():
1042
- dw.wait_for_acks(duration(seconds=flush_s))
1090
+ _wait_for_acks(dw, flush_s, "presence")
1043
1091
  for r in self._recorders:
1044
1092
  r.flush()
1045
1093
  finally:
@@ -158,8 +158,15 @@ def extend(type_name: str, cls: type, idl: Any) -> Extension:
158
158
  files: list = []
159
159
  for inc in re.findall(r'^\s*#include\s+"([^"]+)"', text, re.M):
160
160
  if not (pin.IDL_DIR / inc).is_file():
161
+ # Includes are rooted at the pin, where idlc given a subdirectory
162
+ # with -I would take a bare name. Name the rooted spelling when
163
+ # there is exactly one, rather than leave the author to find it.
164
+ rooted = sorted(str(f.relative_to(pin.IDL_DIR)) for f in
165
+ pin.IDL_DIR.rglob(Path(inc).name))
166
+ hint = (f"; includes are rooted at the pin: did you mean "
167
+ f"{rooted[0]!r}?" if len(rooted) == 1 else "")
161
168
  problems.append(f"the extension's IDL includes {inc!r}, which the "
162
- f"pinned IDL does not have")
169
+ f"pinned IDL does not have{hint}")
163
170
  continue
164
171
  for f in pin.file_closure(inc):
165
172
  if f not in files:
@@ -69,7 +69,8 @@ class Recorder:
69
69
  self._last = t
70
70
  return t
71
71
 
72
- def sample(self, topic: str, fqn: str, sample: Any) -> None:
72
+ def sample(self, topic: str, fqn: str, sample: Any,
73
+ durability: str = "") -> None:
73
74
  with self._lock:
74
75
  if self._closed:
75
76
  return
@@ -82,7 +83,9 @@ class Recorder:
82
83
  if topic not in self._channels:
83
84
  self._channels[topic] = self._w.register_channel(
84
85
  topic=topic, message_encoding="cdr",
85
- schema_id=self._schemas[fqn])
86
+ schema_id=self._schemas[fqn],
87
+ metadata=({mcapmeta.CHANNEL_DURABILITY: durability}
88
+ if durability else {}))
86
89
  t = self._now()
87
90
  self._w.add_message(channel_id=self._channels[topic], log_time=t,
88
91
  publish_time=t, data=sample.serialize(),
@@ -237,9 +237,17 @@ def stamp(when: Any = None) -> Dict[str, Any]:
237
237
  elif isinstance(when, str):
238
238
  w = _dt.datetime.fromisoformat(when.replace("Z", "+00:00"))
239
239
  t = (w if w.tzinfo else w.replace(tzinfo=_dt.timezone.utc)).timestamp()
240
+ elif isinstance(when, int):
241
+ t = when # exact: no float on the way through
240
242
  else:
241
243
  t = float(when)
242
- return {"sec": str(int(t)), "nanosec": int(round((t % 1) * 1e9))}
244
+ # One integer count of nanoseconds, split by divmod, which floors both
245
+ # parts: `nanosec` is always in [0, 10^9), and a time before the epoch
246
+ # keeps its sign in `sec`. Until 0.7.4 the parts were computed apart, so
247
+ # 5.9999999999 gave nanosec 10^9, and -0.25 gave +0.75 s (S16).
248
+ ns = t * 1_000_000_000 if isinstance(t, int) else round(t * 1e9)
249
+ sec, nanosec = divmod(ns, 1_000_000_000)
250
+ return {"sec": str(sec), "nanosec": nanosec}
243
251
 
244
252
 
245
253
  def earth_fixed() -> Dict[str, Any]:
@@ -58,6 +58,11 @@ def build_parser(ap: argparse.ArgumentParser) -> argparse.ArgumentParser:
58
58
  help="a typed SpatialDDS MCAP to seed from. Optional: "
59
59
  "with no recording the registry starts empty and "
60
60
  "takes its types from the IDL pinned in this package.")
61
+ ap.add_argument("--public", action="append", default=[], metavar="FQN",
62
+ help="serve this type's lanes public (repeatable), for a "
63
+ "deployment's own type in --mcap, which is "
64
+ "operator-scoped otherwise: who may read it is the "
65
+ "deployment's call, not the recording's")
61
66
  ap.add_argument("--authority", default=None,
62
67
  help="the DNS name this registry answers as, e.g. "
63
68
  "registry.mytown.example. Every URI it accepts and "
@@ -202,7 +207,7 @@ def main(argv: list[str] | None = None) -> int:
202
207
  try:
203
208
  st = gateway.build_state(
204
209
  a.mcap, a.manifests if a.manifests and a.manifests.is_dir()
205
- else None)
210
+ else None, public=a.public)
206
211
  except gateway.RecordingRefused as e:
207
212
  raise SystemExit(str(e))
208
213
  # The manifest hands clients absolute URLs, so the base has to be the
@@ -351,7 +351,8 @@ def _non_finite(v: Any, at: str = "") -> tuple[str, float] | None:
351
351
 
352
352
 
353
353
  def build_state(path: Path | None, manifests: Path | None = None,
354
- idl_dir: Path | None | object = PINNED) -> State:
354
+ idl_dir: Path | None | object = PINNED,
355
+ public: Iterable[str] = ()) -> State:
355
356
  """The state a recording describes, or an empty registry if given none.
356
357
 
357
358
  A seed is optional because the publish surface makes the recording one
@@ -363,7 +364,16 @@ def build_state(path: Path | None, manifests: Path | None = None,
363
364
  a directory without the IDL in it, leaves the gateway typeless -- which is
364
365
  still a legitimate read-only state, and the refusal that follows belongs
365
366
  where publishing is switched on.
367
+
368
+ A type `DURABILITY` has no row for, a deployment's own, is latched if
369
+ the recording says its writer opened the lane TRANSIENT_LOCAL
370
+ (`mcapmeta.CHANNEL_DURABILITY`), and operator-scoped unless named in
371
+ `public`: who may read a lane is the deployment's call, and the file
372
+ cannot make it. Until 0.7.4 such a lane was always volatile and
373
+ operator-scoped (S20). A type with a row keeps its row: the table is
374
+ the specification's, and a recording does not override it.
366
375
  """
376
+ public = frozenset(public)
367
377
  if path is None:
368
378
  where = pin.IDL_DIR if idl_dir is PINNED else idl_dir
369
379
  model, idl = pin.wire_model(where) if where is not None \
@@ -437,7 +447,14 @@ def build_state(path: Path | None, manifests: Path | None = None,
437
447
  agg = model.get(fqn)
438
448
  keys = [m.name for m in (agg.members if agg else [])
439
449
  if m.is_key]
440
- latched, access = DURABILITY.get(fqn, (False, OPERATOR))
450
+ if fqn in DURABILITY:
451
+ latched, access = DURABILITY[fqn]
452
+ else:
453
+ latched = (ch.metadata.get(mcapmeta.CHANNEL_DURABILITY)
454
+ == "TRANSIENT_LOCAL")
455
+ access = OPERATOR
456
+ if fqn in public:
457
+ access = PUBLIC
441
458
  res = Resource(scene, stream, type_seg, version, ch.topic, fqn,
442
459
  keys[0] if keys else "", access, latched)
443
460
  st.resources[res.path] = res
@@ -497,18 +497,29 @@ def _text_response(request: web.Request, text: str,
497
497
  charset="utf-8", headers=headers)
498
498
 
499
499
 
500
+ def _schemas(request: web.Request) -> dict[str, dict]:
501
+ """The vendored schemas, and one for each served type they lack (S19).
502
+
503
+ N.2: an endpoint MUST publish a schema for every type it serves. A
504
+ deployment's own type is described by the model it is decoded with.
505
+ """
506
+ st: gateway.State = request.app["state"]
507
+ return webjson.served_schemas(st.model,
508
+ {r.fqn for r in st.resources.values()})
509
+
510
+
500
511
  async def schemas_index(request: web.Request) -> web.Response:
501
512
  base = request.app["base"]
502
513
  doc = [{"type": fqn,
503
514
  "url": f"{base}{WELL_KNOWN}/schemas/{webjson.schema_file_for(fqn)}",
504
515
  "digest": f"sha256:{webjson.digest(schema)}"}
505
- for fqn, schema in sorted(webjson.schemas().items())]
516
+ for fqn, schema in sorted(_schemas(request).items())]
506
517
  return _json_response(request, doc, gateway.PUBLIC)
507
518
 
508
519
 
509
520
  async def schema_one(request: web.Request) -> web.Response:
510
521
  name = request.match_info["type"]
511
- for fqn, schema in webjson.schemas().items():
522
+ for fqn, schema in _schemas(request).items():
512
523
  if webjson.schema_file_for(fqn) == name:
513
524
  return _json_response(request, schema, gateway.PUBLIC)
514
525
  raise web.HTTPNotFound(