@bldrs-ai/conway 1.389.1221 → 1.394.1504
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.
- package/README.md +36 -22
- package/compiled/Dist/ConwayGeomWasmNode.js +0 -0
- package/compiled/Dist/ConwayGeomWasmNodeMT.js +0 -0
- package/compiled/Dist/ConwayGeomWasmWeb.js +0 -0
- package/compiled/Dist/ConwayGeomWasmWebMT.js +1 -1
- package/compiled/Dist/ConwayGeomWasmWebMT.wasm +0 -0
- package/compiled/dependencies/conway-geom/Dist/ConwayGeomWasmNode.js +0 -0
- package/compiled/dependencies/conway-geom/Dist/ConwayGeomWasmNodeMT.js +0 -0
- package/compiled/dependencies/conway-geom/Dist/ConwayGeomWasmWeb.js +0 -0
- package/compiled/dependencies/conway-geom/Dist/ConwayGeomWasmWebMT.js +1 -1
- package/compiled/dependencies/conway-geom/Dist/ConwayGeomWasmWebMT.wasm +0 -0
- package/compiled/dependencies/conway-geom/interface/conway_geometry.d.ts +19 -0
- package/compiled/dependencies/conway-geom/interface/conway_geometry.d.ts.map +1 -1
- package/compiled/dependencies/conway-geom/interface/conway_geometry.js +17 -0
- package/compiled/dependencies/conway-geom/interface/parameters/surface_object.d.ts +6 -0
- package/compiled/dependencies/conway-geom/interface/parameters/surface_object.d.ts.map +1 -1
- package/compiled/examples/browser-bundled.cjs +1569 -51
- package/compiled/examples/cli-bundled.cjs +2790 -436
- package/compiled/examples/cli-step-bundled.cjs +1904 -229
- package/compiled/examples/validator-bundled.cjs +1569 -51
- package/compiled/src/AP214E3_2010/AP214E3_2010_gen/b_spline_curve.gen.d.ts +2 -2
- package/compiled/src/AP214E3_2010/AP214E3_2010_gen/b_spline_curve.gen.d.ts.map +1 -1
- package/compiled/src/AP214E3_2010/AP214E3_2010_gen/b_spline_curve.gen.js +2 -2
- package/compiled/src/AP214E3_2010/AP214E3_2010_gen/b_spline_surface.gen.d.ts +3 -3
- package/compiled/src/AP214E3_2010/AP214E3_2010_gen/b_spline_surface.gen.d.ts.map +1 -1
- package/compiled/src/AP214E3_2010/AP214E3_2010_gen/b_spline_surface.gen.js +3 -3
- package/compiled/src/AP214E3_2010/AP214E3_2010_gen/composite_curve.gen.d.ts +1 -1
- package/compiled/src/AP214E3_2010/AP214E3_2010_gen/composite_curve.gen.d.ts.map +1 -1
- package/compiled/src/AP214E3_2010/AP214E3_2010_gen/composite_curve.gen.js +1 -1
- package/compiled/src/AP214E3_2010/AP214E3_2010_gen/offset_curve_2d.gen.d.ts +1 -1
- package/compiled/src/AP214E3_2010/AP214E3_2010_gen/offset_curve_2d.gen.d.ts.map +1 -1
- package/compiled/src/AP214E3_2010/AP214E3_2010_gen/offset_curve_2d.gen.js +1 -1
- package/compiled/src/AP214E3_2010/AP214E3_2010_gen/offset_curve_3d.gen.d.ts +1 -1
- package/compiled/src/AP214E3_2010/AP214E3_2010_gen/offset_curve_3d.gen.d.ts.map +1 -1
- package/compiled/src/AP214E3_2010/AP214E3_2010_gen/offset_curve_3d.gen.js +1 -1
- package/compiled/src/AP214E3_2010/AP214E3_2010_gen/offset_surface.gen.d.ts +1 -1
- package/compiled/src/AP214E3_2010/AP214E3_2010_gen/offset_surface.gen.d.ts.map +1 -1
- package/compiled/src/AP214E3_2010/AP214E3_2010_gen/offset_surface.gen.js +1 -1
- package/compiled/src/AP214E3_2010/AP214E3_2010_gen/presentation_style_assignment.gen.d.ts.map +1 -1
- package/compiled/src/AP214E3_2010/AP214E3_2010_gen/presentation_style_assignment.gen.js +2 -2
- package/compiled/src/AP214E3_2010/AP214E3_2010_gen/schema_ap214.gen.js +11 -11
- package/compiled/src/AP214E3_2010/AP214E3_2010_gen/shape_aspect.gen.d.ts +1 -1
- package/compiled/src/AP214E3_2010/AP214E3_2010_gen/shape_aspect.gen.d.ts.map +1 -1
- package/compiled/src/AP214E3_2010/AP214E3_2010_gen/shape_aspect.gen.js +1 -1
- package/compiled/src/AP214E3_2010/ap214_command_line_main.js +7 -3
- package/compiled/src/AP214E3_2010/ap214_geometry_extraction.d.ts +181 -7
- package/compiled/src/AP214E3_2010/ap214_geometry_extraction.d.ts.map +1 -1
- package/compiled/src/AP214E3_2010/ap214_geometry_extraction.js +906 -137
- package/compiled/src/AP214E3_2010/ap214_geometry_extraction.test.js +10 -2
- package/compiled/src/AP214E3_2010/ap214_logical_attribute.test.d.ts +2 -0
- package/compiled/src/AP214E3_2010/ap214_logical_attribute.test.d.ts.map +1 -0
- package/compiled/src/AP214E3_2010/ap214_logical_attribute.test.js +114 -0
- package/compiled/src/AP214E3_2010/ap214_model_geometry.d.ts +6 -0
- package/compiled/src/AP214E3_2010/ap214_model_geometry.d.ts.map +1 -1
- package/compiled/src/AP214E3_2010/ap214_model_geometry.js +9 -0
- package/compiled/src/AP214E3_2010/ap214_pcurve_basis_surfaces.test.d.ts +2 -0
- package/compiled/src/AP214E3_2010/ap214_pcurve_basis_surfaces.test.d.ts.map +1 -0
- package/compiled/src/AP214E3_2010/ap214_pcurve_basis_surfaces.test.js +233 -0
- package/compiled/src/AP214E3_2010/ap214_sphere_vertex_loop.test.d.ts +2 -0
- package/compiled/src/AP214E3_2010/ap214_sphere_vertex_loop.test.d.ts.map +1 -0
- package/compiled/src/AP214E3_2010/ap214_sphere_vertex_loop.test.js +147 -0
- package/compiled/src/AP214E3_2010/ap214_step_model.d.ts +2 -1
- package/compiled/src/AP214E3_2010/ap214_step_model.d.ts.map +1 -1
- package/compiled/src/AP214E3_2010/ap214_step_model.js +1 -1
- package/compiled/src/AP214E3_2010/ap214_typed_select_enum.test.d.ts +2 -0
- package/compiled/src/AP214E3_2010/ap214_typed_select_enum.test.d.ts.map +1 -0
- package/compiled/src/AP214E3_2010/ap214_typed_select_enum.test.js +65 -0
- package/compiled/src/AP214E3_2010/ap214_unit_scale.test.d.ts +2 -0
- package/compiled/src/AP214E3_2010/ap214_unit_scale.test.d.ts.map +1 -0
- package/compiled/src/AP214E3_2010/ap214_unit_scale.test.js +111 -0
- package/compiled/src/compat/web-ifc/ap214_streamed_open.test.d.ts +2 -0
- package/compiled/src/compat/web-ifc/ap214_streamed_open.test.d.ts.map +1 -0
- package/compiled/src/compat/web-ifc/ap214_streamed_open.test.js +190 -0
- package/compiled/src/compat/web-ifc/coordination_export_order.test.d.ts +2 -0
- package/compiled/src/compat/web-ifc/coordination_export_order.test.d.ts.map +1 -0
- package/compiled/src/compat/web-ifc/coordination_export_order.test.js +110 -0
- package/compiled/src/compat/web-ifc/coordination_f64.d.ts +165 -0
- package/compiled/src/compat/web-ifc/coordination_f64.d.ts.map +1 -0
- package/compiled/src/compat/web-ifc/coordination_f64.js +286 -0
- package/compiled/src/compat/web-ifc/coordination_f64.test.d.ts +2 -0
- package/compiled/src/compat/web-ifc/coordination_f64.test.d.ts.map +1 -0
- package/compiled/src/compat/web-ifc/coordination_f64.test.js +225 -0
- package/compiled/src/compat/web-ifc/ifc_api.d.ts +154 -0
- package/compiled/src/compat/web-ifc/ifc_api.d.ts.map +1 -1
- package/compiled/src/compat/web-ifc/ifc_api.js +211 -3
- package/compiled/src/compat/web-ifc/ifc_api_deferred_open.test.d.ts +2 -0
- package/compiled/src/compat/web-ifc/ifc_api_deferred_open.test.d.ts.map +1 -0
- package/compiled/src/compat/web-ifc/ifc_api_deferred_open.test.js +288 -0
- package/compiled/src/compat/web-ifc/ifc_api_model_passthrough.d.ts +39 -0
- package/compiled/src/compat/web-ifc/ifc_api_model_passthrough.d.ts.map +1 -1
- package/compiled/src/compat/web-ifc/ifc_api_model_passthrough_factory.d.ts +30 -0
- package/compiled/src/compat/web-ifc/ifc_api_model_passthrough_factory.d.ts.map +1 -1
- package/compiled/src/compat/web-ifc/ifc_api_model_passthrough_factory.js +83 -0
- package/compiled/src/compat/web-ifc/ifc_api_preview_channel.test.d.ts +2 -0
- package/compiled/src/compat/web-ifc/ifc_api_preview_channel.test.d.ts.map +1 -0
- package/compiled/src/compat/web-ifc/ifc_api_preview_channel.test.js +383 -0
- package/compiled/src/compat/web-ifc/ifc_api_preview_coordination.test.d.ts +2 -0
- package/compiled/src/compat/web-ifc/ifc_api_preview_coordination.test.d.ts.map +1 -0
- package/compiled/src/compat/web-ifc/ifc_api_preview_coordination.test.js +124 -0
- package/compiled/src/compat/web-ifc/ifc_api_proxy_ap214.d.ts +132 -0
- package/compiled/src/compat/web-ifc/ifc_api_proxy_ap214.d.ts.map +1 -1
- package/compiled/src/compat/web-ifc/ifc_api_proxy_ap214.js +488 -92
- package/compiled/src/compat/web-ifc/ifc_api_proxy_ifc.d.ts +268 -0
- package/compiled/src/compat/web-ifc/ifc_api_proxy_ifc.d.ts.map +1 -1
- package/compiled/src/compat/web-ifc/ifc_api_proxy_ifc.js +996 -149
- package/compiled/src/compat/web-ifc/ifc_api_streamed_open.test.d.ts +2 -0
- package/compiled/src/compat/web-ifc/ifc_api_streamed_open.test.d.ts.map +1 -0
- package/compiled/src/compat/web-ifc/ifc_api_streamed_open.test.js +129 -0
- package/compiled/src/compat/web-ifc/spatial_imposter.d.ts +134 -0
- package/compiled/src/compat/web-ifc/spatial_imposter.d.ts.map +1 -0
- package/compiled/src/compat/web-ifc/spatial_imposter.js +933 -0
- package/compiled/src/compat/web-ifc/spatial_imposter.test.d.ts +2 -0
- package/compiled/src/compat/web-ifc/spatial_imposter.test.d.ts.map +1 -0
- package/compiled/src/compat/web-ifc/spatial_imposter.test.js +467 -0
- package/compiled/src/compat/web-ifc/store_preview_channel.d.ts +187 -0
- package/compiled/src/compat/web-ifc/store_preview_channel.d.ts.map +1 -0
- package/compiled/src/compat/web-ifc/store_preview_channel.js +524 -0
- package/compiled/src/compat/web-ifc/store_preview_channel.test.d.ts +2 -0
- package/compiled/src/compat/web-ifc/store_preview_channel.test.d.ts.map +1 -0
- package/compiled/src/compat/web-ifc/store_preview_channel.test.js +234 -0
- package/compiled/src/compat/web-ifc/streamed_preview_channel.d.ts +306 -0
- package/compiled/src/compat/web-ifc/streamed_preview_channel.d.ts.map +1 -0
- package/compiled/src/compat/web-ifc/streamed_preview_channel.js +580 -0
- package/compiled/src/compat/web-ifc/web_wasm_directory.test.d.ts +2 -0
- package/compiled/src/compat/web-ifc/web_wasm_directory.test.d.ts.map +1 -0
- package/compiled/src/compat/web-ifc/web_wasm_directory.test.js +22 -0
- package/compiled/src/core/composed_model_skeleton.d.ts +63 -0
- package/compiled/src/core/composed_model_skeleton.d.ts.map +1 -0
- package/compiled/src/core/composed_model_skeleton.js +66 -0
- package/compiled/src/core/composed_model_skeleton.test.d.ts +2 -0
- package/compiled/src/core/composed_model_skeleton.test.d.ts.map +1 -0
- package/compiled/src/core/composed_model_skeleton.test.js +69 -0
- package/compiled/src/core/demand_geometry_queue.d.ts +147 -0
- package/compiled/src/core/demand_geometry_queue.d.ts.map +1 -0
- package/compiled/src/core/demand_geometry_queue.js +223 -0
- package/compiled/src/core/demand_geometry_queue.test.d.ts +2 -0
- package/compiled/src/core/demand_geometry_queue.test.d.ts.map +1 -0
- package/compiled/src/core/demand_geometry_queue.test.js +133 -0
- package/compiled/src/core/demand_residency_pump.d.ts +93 -0
- package/compiled/src/core/demand_residency_pump.d.ts.map +1 -0
- package/compiled/src/core/demand_residency_pump.js +120 -0
- package/compiled/src/core/demand_residency_pump.test.d.ts +2 -0
- package/compiled/src/core/demand_residency_pump.test.d.ts.map +1 -0
- package/compiled/src/core/demand_residency_pump.test.js +148 -0
- package/compiled/src/core/geometry_tile_bindings.d.ts +110 -0
- package/compiled/src/core/geometry_tile_bindings.d.ts.map +1 -0
- package/compiled/src/core/geometry_tile_bindings.js +76 -0
- package/compiled/src/core/geometry_tile_bindings.test.d.ts +2 -0
- package/compiled/src/core/geometry_tile_bindings.test.d.ts.map +1 -0
- package/compiled/src/core/geometry_tile_bindings.test.js +211 -0
- package/compiled/src/core/geometry_tile_pool.d.ts +99 -0
- package/compiled/src/core/geometry_tile_pool.d.ts.map +1 -0
- package/compiled/src/core/geometry_tile_pool.js +104 -0
- package/compiled/src/core/geometry_tile_pool.test.d.ts +2 -0
- package/compiled/src/core/geometry_tile_pool.test.d.ts.map +1 -0
- package/compiled/src/core/geometry_tile_pool.test.js +180 -0
- package/compiled/src/core/mem/chunked_pool.d.ts +116 -0
- package/compiled/src/core/mem/chunked_pool.d.ts.map +1 -0
- package/compiled/src/core/mem/chunked_pool.js +149 -0
- package/compiled/src/core/mem/chunked_pool.test.d.ts +2 -0
- package/compiled/src/core/mem/chunked_pool.test.d.ts.map +1 -0
- package/compiled/src/core/mem/chunked_pool.test.js +63 -0
- package/compiled/src/core/mem/shared_asset_pool.d.ts +82 -0
- package/compiled/src/core/mem/shared_asset_pool.d.ts.map +1 -0
- package/compiled/src/core/mem/shared_asset_pool.js +113 -0
- package/compiled/src/core/mem/shared_asset_pool.test.d.ts +2 -0
- package/compiled/src/core/mem/shared_asset_pool.test.d.ts.map +1 -0
- package/compiled/src/core/mem/shared_asset_pool.test.js +49 -0
- package/compiled/src/core/model_registry.d.ts +87 -0
- package/compiled/src/core/model_registry.d.ts.map +1 -0
- package/compiled/src/core/model_registry.js +101 -0
- package/compiled/src/core/model_registry.test.d.ts +2 -0
- package/compiled/src/core/model_registry.test.d.ts.map +1 -0
- package/compiled/src/core/model_registry.test.js +60 -0
- package/compiled/src/core/model_uri.d.ts +57 -0
- package/compiled/src/core/model_uri.d.ts.map +1 -0
- package/compiled/src/core/model_uri.js +101 -0
- package/compiled/src/core/model_uri.test.d.ts +2 -0
- package/compiled/src/core/model_uri.test.d.ts.map +1 -0
- package/compiled/src/core/model_uri.test.js +49 -0
- package/compiled/src/core/progress.d.ts +16 -2
- package/compiled/src/core/progress.d.ts.map +1 -1
- package/compiled/src/core/progress.js +31 -5
- package/compiled/src/core/progress.test.js +11 -2
- package/compiled/src/core/progress_log.d.ts +1 -0
- package/compiled/src/core/progress_log.d.ts.map +1 -1
- package/compiled/src/core/progress_log.js +7 -2
- package/compiled/src/core/progress_log.test.js +21 -0
- package/compiled/src/core/shared_byte_budget.d.ts +60 -0
- package/compiled/src/core/shared_byte_budget.d.ts.map +1 -0
- package/compiled/src/core/shared_byte_budget.js +85 -0
- package/compiled/src/core/shared_byte_budget.test.d.ts +2 -0
- package/compiled/src/core/shared_byte_budget.test.d.ts.map +1 -0
- package/compiled/src/core/shared_byte_budget.test.js +55 -0
- package/compiled/src/core/wasm_heap.d.ts +208 -0
- package/compiled/src/core/wasm_heap.d.ts.map +1 -0
- package/compiled/src/core/wasm_heap.js +417 -0
- package/compiled/src/core/wasm_heap.test.d.ts +2 -0
- package/compiled/src/core/wasm_heap.test.d.ts.map +1 -0
- package/compiled/src/core/wasm_heap.test.js +546 -0
- package/compiled/src/demand/index.d.ts +23 -0
- package/compiled/src/demand/index.d.ts.map +1 -0
- package/compiled/src/demand/index.js +22 -0
- package/compiled/src/ifc/ifc4_gen/IfcLogical.gen.d.ts +1 -1
- package/compiled/src/ifc/ifc4_gen/IfcLogical.gen.d.ts.map +1 -1
- package/compiled/src/ifc/ifc4_gen/IfcLogical.gen.js +1 -1
- package/compiled/src/ifc/ifc4_gen/IfcPresentationStyleAssignment.gen.d.ts.map +1 -1
- package/compiled/src/ifc/ifc4_gen/IfcPresentationStyleAssignment.gen.js +2 -2
- package/compiled/src/ifc/ifc4_gen/IfcRelInterferesElements.gen.d.ts +1 -1
- package/compiled/src/ifc/ifc4_gen/IfcRelInterferesElements.gen.d.ts.map +1 -1
- package/compiled/src/ifc/ifc4_gen/IfcRelInterferesElements.gen.js +1 -1
- package/compiled/src/ifc/ifc4_gen/schema_ifc.gen.js +2 -2
- package/compiled/src/ifc/ifc_demand_extraction.test.d.ts +2 -0
- package/compiled/src/ifc/ifc_demand_extraction.test.d.ts.map +1 -0
- package/compiled/src/ifc/ifc_demand_extraction.test.js +251 -0
- package/compiled/src/ifc/ifc_geometry_extraction.d.ts +367 -6
- package/compiled/src/ifc/ifc_geometry_extraction.d.ts.map +1 -1
- package/compiled/src/ifc/ifc_geometry_extraction.js +1322 -389
- package/compiled/src/ifc/ifc_grid_placement.test.d.ts +2 -0
- package/compiled/src/ifc/ifc_grid_placement.test.d.ts.map +1 -0
- package/compiled/src/ifc/ifc_grid_placement.test.js +261 -0
- package/compiled/src/ifc/ifc_model_geometry.d.ts +12 -0
- package/compiled/src/ifc/ifc_model_geometry.d.ts.map +1 -1
- package/compiled/src/ifc/ifc_model_geometry.js +15 -0
- package/compiled/src/ifc/ifc_regression_batch_main.js +210 -12
- package/compiled/src/ifc/ifc_spatial_skeleton.d.ts +145 -0
- package/compiled/src/ifc/ifc_spatial_skeleton.d.ts.map +1 -0
- package/compiled/src/ifc/ifc_spatial_skeleton.js +266 -0
- package/compiled/src/ifc/ifc_spatial_skeleton.test.d.ts +2 -0
- package/compiled/src/ifc/ifc_spatial_skeleton.test.d.ts.map +1 -0
- package/compiled/src/ifc/ifc_spatial_skeleton.test.js +199 -0
- package/compiled/src/ifc/ifc_step_model.d.ts +6 -2
- package/compiled/src/ifc/ifc_step_model.d.ts.map +1 -1
- package/compiled/src/ifc/ifc_step_model.js +6 -4
- package/compiled/src/ifc/ifc_step_parser.d.ts +58 -0
- package/compiled/src/ifc/ifc_step_parser.d.ts.map +1 -1
- package/compiled/src/ifc/ifc_step_parser.js +64 -0
- package/compiled/src/ifc/ifc_stream_model.test.d.ts +2 -0
- package/compiled/src/ifc/ifc_stream_model.test.d.ts.map +1 -0
- package/compiled/src/ifc/ifc_stream_model.test.js +70 -0
- package/compiled/src/ifc/ifc_stream_open.d.ts +131 -0
- package/compiled/src/ifc/ifc_stream_open.d.ts.map +1 -0
- package/compiled/src/ifc/ifc_stream_open.js +116 -0
- package/compiled/src/ifc/ifc_stream_open.test.d.ts +2 -0
- package/compiled/src/ifc/ifc_stream_open.test.d.ts.map +1 -0
- package/compiled/src/ifc/ifc_stream_open.test.js +230 -0
- package/compiled/src/ifc/ifc_tile_extractor.d.ts +84 -0
- package/compiled/src/ifc/ifc_tile_extractor.d.ts.map +1 -0
- package/compiled/src/ifc/ifc_tile_extractor.js +134 -0
- package/compiled/src/ifc/ifc_tile_extractor.test.d.ts +2 -0
- package/compiled/src/ifc/ifc_tile_extractor.test.d.ts.map +1 -0
- package/compiled/src/ifc/ifc_tile_extractor.test.js +118 -0
- package/compiled/src/ifc/ifc_typed_select_enum.test.d.ts +2 -0
- package/compiled/src/ifc/ifc_typed_select_enum.test.d.ts.map +1 -0
- package/compiled/src/ifc/ifc_typed_select_enum.test.js +60 -0
- package/compiled/src/index.d.ts +21 -1
- package/compiled/src/index.d.ts.map +1 -1
- package/compiled/src/index.js +28 -1
- package/compiled/src/logging/logger.d.ts +16 -2
- package/compiled/src/logging/logger.d.ts.map +1 -1
- package/compiled/src/logging/logger.js +34 -7
- package/compiled/src/logging/logger.test.js +30 -0
- package/compiled/src/mem/index.d.ts +17 -0
- package/compiled/src/mem/index.d.ts.map +1 -0
- package/compiled/src/mem/index.js +16 -0
- package/compiled/src/namespace_surface.test.d.ts +2 -0
- package/compiled/src/namespace_surface.test.d.ts.map +1 -0
- package/compiled/src/namespace_surface.test.js +43 -0
- package/compiled/src/parsing/parsing_buffer.d.ts +23 -0
- package/compiled/src/parsing/parsing_buffer.d.ts.map +1 -1
- package/compiled/src/parsing/parsing_buffer.js +31 -0
- package/compiled/src/scripts/model_report_displacement.test.d.ts +2 -0
- package/compiled/src/scripts/model_report_displacement.test.d.ts.map +1 -0
- package/compiled/src/scripts/model_report_displacement.test.js +104 -0
- package/compiled/src/scripts/render_glb_paths.test.d.ts +2 -0
- package/compiled/src/scripts/render_glb_paths.test.d.ts.map +1 -0
- package/compiled/src/scripts/render_glb_paths.test.js +115 -0
- package/compiled/src/step/indexing/step_type_indexer.d.ts +20 -0
- package/compiled/src/step/indexing/step_type_indexer.d.ts.map +1 -1
- package/compiled/src/step/indexing/step_type_indexer.js +67 -0
- package/compiled/src/step/parsing/byte_source.d.ts +153 -0
- package/compiled/src/step/parsing/byte_source.d.ts.map +1 -0
- package/compiled/src/step/parsing/byte_source.js +106 -0
- package/compiled/src/step/parsing/byte_source.test.d.ts +2 -0
- package/compiled/src/step/parsing/byte_source.test.d.ts.map +1 -0
- package/compiled/src/step/parsing/byte_source.test.js +47 -0
- package/compiled/src/step/parsing/byte_source_node.d.ts +41 -0
- package/compiled/src/step/parsing/byte_source_node.d.ts.map +1 -0
- package/compiled/src/step/parsing/byte_source_node.js +54 -0
- package/compiled/src/step/parsing/columnar_index.d.ts +103 -0
- package/compiled/src/step/parsing/columnar_index.d.ts.map +1 -0
- package/compiled/src/step/parsing/columnar_index.js +224 -0
- package/compiled/src/step/parsing/columnar_index.test.d.ts +2 -0
- package/compiled/src/step/parsing/columnar_index.test.d.ts.map +1 -0
- package/compiled/src/step/parsing/columnar_index.test.js +155 -0
- package/compiled/src/step/parsing/cross_reference_registry.d.ts +95 -0
- package/compiled/src/step/parsing/cross_reference_registry.d.ts.map +1 -0
- package/compiled/src/step/parsing/cross_reference_registry.js +91 -0
- package/compiled/src/step/parsing/cross_reference_registry.test.d.ts +2 -0
- package/compiled/src/step/parsing/cross_reference_registry.test.d.ts.map +1 -0
- package/compiled/src/step/parsing/cross_reference_registry.test.js +66 -0
- package/compiled/src/step/parsing/decode_utf8.d.ts +15 -0
- package/compiled/src/step/parsing/decode_utf8.d.ts.map +1 -1
- package/compiled/src/step/parsing/decode_utf8.js +28 -0
- package/compiled/src/step/parsing/express_ref_scan.d.ts +20 -0
- package/compiled/src/step/parsing/express_ref_scan.d.ts.map +1 -0
- package/compiled/src/step/parsing/express_ref_scan.js +91 -0
- package/compiled/src/step/parsing/express_ref_scan.test.d.ts +2 -0
- package/compiled/src/step/parsing/express_ref_scan.test.d.ts.map +1 -0
- package/compiled/src/step/parsing/express_ref_scan.test.js +28 -0
- package/compiled/src/step/parsing/index_sidecar.d.ts +81 -0
- package/compiled/src/step/parsing/index_sidecar.d.ts.map +1 -0
- package/compiled/src/step/parsing/index_sidecar.js +283 -0
- package/compiled/src/step/parsing/index_sidecar.test.d.ts +2 -0
- package/compiled/src/step/parsing/index_sidecar.test.d.ts.map +1 -0
- package/compiled/src/step/parsing/index_sidecar.test.js +122 -0
- package/compiled/src/step/parsing/inf_recovery_index.test.d.ts +2 -0
- package/compiled/src/step/parsing/inf_recovery_index.test.d.ts.map +1 -0
- package/compiled/src/step/parsing/inf_recovery_index.test.js +108 -0
- package/compiled/src/step/parsing/prefix_type_index.d.ts +124 -0
- package/compiled/src/step/parsing/prefix_type_index.d.ts.map +1 -0
- package/compiled/src/step/parsing/prefix_type_index.js +207 -0
- package/compiled/src/step/parsing/prefix_type_index.test.d.ts +2 -0
- package/compiled/src/step/parsing/prefix_type_index.test.d.ts.map +1 -0
- package/compiled/src/step/parsing/prefix_type_index.test.js +298 -0
- package/compiled/src/step/parsing/range_byte_source.d.ts +69 -0
- package/compiled/src/step/parsing/range_byte_source.d.ts.map +1 -0
- package/compiled/src/step/parsing/range_byte_source.js +78 -0
- package/compiled/src/step/parsing/range_byte_source.test.d.ts +2 -0
- package/compiled/src/step/parsing/range_byte_source.test.d.ts.map +1 -0
- package/compiled/src/step/parsing/range_byte_source.test.js +70 -0
- package/compiled/src/step/parsing/record_event.d.ts +23 -0
- package/compiled/src/step/parsing/record_event.d.ts.map +1 -0
- package/compiled/src/step/parsing/record_event.js +1 -0
- package/compiled/src/step/parsing/record_field_cursor.d.ts +94 -0
- package/compiled/src/step/parsing/record_field_cursor.d.ts.map +1 -0
- package/compiled/src/step/parsing/record_field_cursor.js +189 -0
- package/compiled/src/step/parsing/step_deserialization_functions.d.ts +29 -0
- package/compiled/src/step/parsing/step_deserialization_functions.d.ts.map +1 -1
- package/compiled/src/step/parsing/step_deserialization_functions.js +45 -0
- package/compiled/src/step/parsing/step_deserialization_functions.test.js +59 -1
- package/compiled/src/step/parsing/step_parser.d.ts +66 -1
- package/compiled/src/step/parsing/step_parser.d.ts.map +1 -1
- package/compiled/src/step/parsing/step_parser.js +164 -10
- package/compiled/src/step/parsing/step_string_parser.js +8 -8
- package/compiled/src/step/parsing/streaming_index_builder.d.ts +141 -0
- package/compiled/src/step/parsing/streaming_index_builder.d.ts.map +1 -0
- package/compiled/src/step/parsing/streaming_index_builder.js +280 -0
- package/compiled/src/step/parsing/streaming_index_builder.test.d.ts +2 -0
- package/compiled/src/step/parsing/streaming_index_builder.test.d.ts.map +1 -0
- package/compiled/src/step/parsing/streaming_index_builder.test.js +107 -0
- package/compiled/src/step/parsing/streaming_index_builder_async.test.d.ts +2 -0
- package/compiled/src/step/parsing/streaming_index_builder_async.test.d.ts.map +1 -0
- package/compiled/src/step/parsing/streaming_index_builder_async.test.js +109 -0
- package/compiled/src/step/parsing/streaming_record_dispatcher.d.ts +71 -0
- package/compiled/src/step/parsing/streaming_record_dispatcher.d.ts.map +1 -0
- package/compiled/src/step/parsing/streaming_record_dispatcher.js +88 -0
- package/compiled/src/step/parsing/streaming_record_dispatcher.test.d.ts +2 -0
- package/compiled/src/step/parsing/streaming_record_dispatcher.test.d.ts.map +1 -0
- package/compiled/src/step/parsing/streaming_record_dispatcher.test.js +66 -0
- package/compiled/src/step/parsing/uint32_sink.d.ts +72 -0
- package/compiled/src/step/parsing/uint32_sink.d.ts.map +1 -0
- package/compiled/src/step/parsing/uint32_sink.js +180 -0
- package/compiled/src/step/parsing/uint32_sink.test.d.ts +2 -0
- package/compiled/src/step/parsing/uint32_sink.test.d.ts.map +1 -0
- package/compiled/src/step/parsing/uint32_sink.test.js +199 -0
- package/compiled/src/step/step_buffer_provider.d.ts +48 -3
- package/compiled/src/step/step_buffer_provider.d.ts.map +1 -1
- package/compiled/src/step/step_buffer_provider.js +60 -11
- package/compiled/src/step/step_buffer_provider.test.js +14 -0
- package/compiled/src/step/step_entity_base.d.ts +65 -3
- package/compiled/src/step/step_entity_base.d.ts.map +1 -1
- package/compiled/src/step/step_entity_base.js +135 -3
- package/compiled/src/step/step_model_base.d.ts +221 -8
- package/compiled/src/step/step_model_base.d.ts.map +1 -1
- package/compiled/src/step/step_model_base.js +542 -7
- package/compiled/src/stream/index.d.ts +34 -0
- package/compiled/src/stream/index.d.ts.map +1 -0
- package/compiled/src/stream/index.js +32 -0
- package/compiled/src/version/version.js +1 -1
- package/compiled/tsconfig.tsbuildinfo +1 -1
- package/package.json +16 -1
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/* eslint-disable no-magic-numbers */
|
|
2
|
+
// M4: the index sidecar must serialise a model's parse index and deserialise
|
|
3
|
+
// it back byte-identically, so an index-first open reconstructs the entity
|
|
4
|
+
// index without re-scanning the source — and it must refuse to be trusted when
|
|
5
|
+
// the source it was built from no longer matches (hash / length handshake).
|
|
6
|
+
import * as fs from "fs";
|
|
7
|
+
import { describe, expect, test } from "@jest/globals";
|
|
8
|
+
import ParsingBuffer from "../../parsing/parsing_buffer.js";
|
|
9
|
+
import IfcStepParser from "../../ifc/ifc_step_parser.js";
|
|
10
|
+
import { deserializeIndexSidecar, deserializeIndexSidecarToColumns, hashSource, serializeIndexSidecar, serializeIndexSidecarFromColumns, sidecarMatchesSource, } from "./index_sidecar.js";
|
|
11
|
+
import { BufferByteSource } from "./byte_source.js";
|
|
12
|
+
import { buildColumnarIndexStreaming } from "./streaming_index_builder.js";
|
|
13
|
+
/**
|
|
14
|
+
* Parse index.ifc resident and return its top-level element index plus the
|
|
15
|
+
* source bytes it was built from.
|
|
16
|
+
*
|
|
17
|
+
* @return {object} `{ bytes, elements }`.
|
|
18
|
+
*/
|
|
19
|
+
function residentIndex() {
|
|
20
|
+
const bytes = new Uint8Array(fs.readFileSync("data/index.ifc"));
|
|
21
|
+
const input = new ParsingBuffer(bytes);
|
|
22
|
+
IfcStepParser.Instance.parseHeader(input);
|
|
23
|
+
const [index,] = IfcStepParser.Instance.parseDataBlock(input);
|
|
24
|
+
return { bytes, elements: index.elements };
|
|
25
|
+
}
|
|
26
|
+
describe("index sidecar", () => {
|
|
27
|
+
test("round-trips the top-level index byte-identically", () => {
|
|
28
|
+
const { bytes, elements } = residentIndex();
|
|
29
|
+
const hash = hashSource(bytes);
|
|
30
|
+
const blob = serializeIndexSidecar(elements, bytes.byteLength, hash);
|
|
31
|
+
const decoded = deserializeIndexSidecar(blob);
|
|
32
|
+
expect(decoded.sourceByteLength).toBe(bytes.byteLength);
|
|
33
|
+
expect(decoded.sourceHash).toBe(hash);
|
|
34
|
+
expect(decoded.elements.length).toBe(elements.length);
|
|
35
|
+
for (let where = 0; where < elements.length; ++where) {
|
|
36
|
+
const original = elements[where];
|
|
37
|
+
const restored = decoded.elements[where];
|
|
38
|
+
expect(restored.address).toBe(original.address);
|
|
39
|
+
expect(restored.length).toBe(original.length);
|
|
40
|
+
expect(restored.expressID).toBe(original.expressID);
|
|
41
|
+
// typeID round-trips including the undefined case (stored as -1).
|
|
42
|
+
expect(restored.typeID).toBe(original.typeID);
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
test("accepts a sidecar whose hash and length match the source", () => {
|
|
46
|
+
const { bytes, elements } = residentIndex();
|
|
47
|
+
const hash = hashSource(bytes);
|
|
48
|
+
const decoded = deserializeIndexSidecar(serializeIndexSidecar(elements, bytes.byteLength, hash));
|
|
49
|
+
expect(sidecarMatchesSource(decoded, bytes.byteLength, hash)).toBe(true);
|
|
50
|
+
});
|
|
51
|
+
test("rejects a sidecar when the source bytes changed (hash mismatch)", () => {
|
|
52
|
+
const { bytes, elements } = residentIndex();
|
|
53
|
+
const hash = hashSource(bytes);
|
|
54
|
+
const decoded = deserializeIndexSidecar(serializeIndexSidecar(elements, bytes.byteLength, hash));
|
|
55
|
+
// Same length, one byte flipped → different hash → must not be trusted.
|
|
56
|
+
const mutated = bytes.slice();
|
|
57
|
+
mutated[Math.floor(mutated.length / 2)] ^= 255;
|
|
58
|
+
expect(sidecarMatchesSource(decoded, mutated.byteLength, hashSource(mutated)))
|
|
59
|
+
.toBe(false);
|
|
60
|
+
});
|
|
61
|
+
test("rejects a sidecar when the source length changed", () => {
|
|
62
|
+
const { bytes, elements } = residentIndex();
|
|
63
|
+
const hash = hashSource(bytes);
|
|
64
|
+
const decoded = deserializeIndexSidecar(serializeIndexSidecar(elements, bytes.byteLength, hash));
|
|
65
|
+
expect(sidecarMatchesSource(decoded, bytes.byteLength + 1, hash))
|
|
66
|
+
.toBe(false);
|
|
67
|
+
});
|
|
68
|
+
test("hashSource is deterministic and sensitive to content", () => {
|
|
69
|
+
const a = new Uint8Array([1, 2, 3, 4, 5]);
|
|
70
|
+
const b = new Uint8Array([1, 2, 3, 4, 5]);
|
|
71
|
+
const c = new Uint8Array([1, 2, 3, 4, 6]);
|
|
72
|
+
expect(hashSource(a)).toBe(hashSource(b));
|
|
73
|
+
expect(hashSource(a)).not.toBe(hashSource(c));
|
|
74
|
+
});
|
|
75
|
+
test("preserves undefined typeID through the -1 sentinel", () => {
|
|
76
|
+
const elements = [
|
|
77
|
+
{ address: 0, length: 10, typeID: 7, expressID: 1 },
|
|
78
|
+
{ address: 10, length: 20, typeID: void 0, expressID: 2 },
|
|
79
|
+
{ address: 30, length: 5, typeID: 0, expressID: 3 },
|
|
80
|
+
];
|
|
81
|
+
const decoded = deserializeIndexSidecar(serializeIndexSidecar(elements, 35, 0));
|
|
82
|
+
expect(decoded.elements[0].typeID).toBe(7);
|
|
83
|
+
expect(decoded.elements[1].typeID).toBe(void 0);
|
|
84
|
+
expect(decoded.elements[2].typeID).toBe(0);
|
|
85
|
+
});
|
|
86
|
+
test("throws on a blob with bad magic", () => {
|
|
87
|
+
const garbage = new Uint8Array(32);
|
|
88
|
+
expect(() => deserializeIndexSidecar(garbage)).toThrow(/magic/);
|
|
89
|
+
});
|
|
90
|
+
test("round-trips an empty index", () => {
|
|
91
|
+
const decoded = deserializeIndexSidecar(serializeIndexSidecar([], 0, 0));
|
|
92
|
+
expect(decoded.elements.length).toBe(0);
|
|
93
|
+
});
|
|
94
|
+
test("columns-form serialize is byte-identical to the object-form blob (M7)", () => {
|
|
95
|
+
const { bytes, elements } = residentIndex();
|
|
96
|
+
const hash = hashSource(bytes);
|
|
97
|
+
const { columns } = buildColumnarIndexStreaming(new BufferByteSource(bytes),
|
|
98
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
99
|
+
IfcStepParser.Instance, 4096);
|
|
100
|
+
const fromObjects = serializeIndexSidecar(elements, bytes.byteLength, hash);
|
|
101
|
+
const fromColumns = serializeIndexSidecarFromColumns(columns, bytes.byteLength, hash);
|
|
102
|
+
expect(fromColumns).toEqual(fromObjects);
|
|
103
|
+
});
|
|
104
|
+
test("deserializes straight to columns for the index-first open (M7)", () => {
|
|
105
|
+
const { bytes, elements } = residentIndex();
|
|
106
|
+
const hash = hashSource(bytes);
|
|
107
|
+
const blob = serializeIndexSidecar(elements, bytes.byteLength, hash);
|
|
108
|
+
const restored = deserializeIndexSidecarToColumns(blob);
|
|
109
|
+
expect(restored.sourceByteLength).toBe(bytes.byteLength);
|
|
110
|
+
expect(restored.sourceHash).toBe(hash);
|
|
111
|
+
expect(restored.columns.count).toBe(elements.length);
|
|
112
|
+
expect(restored.columns.firstInlineElement).toBe(elements.length);
|
|
113
|
+
expect(restored.columns.expressIdsSorted).toBe(true);
|
|
114
|
+
for (let where = 0; where < elements.length; ++where) {
|
|
115
|
+
expect(restored.columns.address[where]).toBe(elements[where].address);
|
|
116
|
+
expect(restored.columns.length[where]).toBe(elements[where].length);
|
|
117
|
+
expect(restored.columns.expressID[where]).toBe(elements[where].expressID);
|
|
118
|
+
expect(restored.columns.typeID[where])
|
|
119
|
+
.toBe(elements[where].typeID ?? -1);
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inf_recovery_index.test.d.ts","sourceRoot":"","sources":["../../../../src/step/parsing/inf_recovery_index.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
// `INF` where a real belongs is not valid ISO 10303-21, but exporters emit it
|
|
2
|
+
// for unbounded quantities and the parser recovers from it. The recovery used
|
|
3
|
+
// to stamp the offending record's express ID over every top-level entry
|
|
4
|
+
// indexed so far in the block, which corrupted every lookup that followed.
|
|
5
|
+
//
|
|
6
|
+
// It stayed hidden only because no public corpus model reaches this recovery.
|
|
7
|
+
// Not because a failed parse is harmless: the stamping ran before the rollback
|
|
8
|
+
// and the INF check, so any top-level inline-instance failure poisoned the
|
|
9
|
+
// index, and parseDataToModel builds a model from those elements whatever the
|
|
10
|
+
// ParseResult. A private model reaches it - `IFCQUANTITYLENGTH(...,INF,$)`
|
|
11
|
+
// from a Swiss architectural export - where it cost 3,116 entities and
|
|
12
|
+
// produced 630 relationship failures all reporting the same express ID.
|
|
13
|
+
// See bldrs-ai/conway#412, #481, #482.
|
|
14
|
+
//
|
|
15
|
+
// Parser-only, so no geometry wasm is initialized here.
|
|
16
|
+
import { describe, expect, test } from "@jest/globals";
|
|
17
|
+
import IfcStepParser from "../../ifc/ifc_step_parser.js";
|
|
18
|
+
import ParsingBuffer from "../../parsing/parsing_buffer.js";
|
|
19
|
+
import { ParseResult } from "./step_parser.js";
|
|
20
|
+
const parser = IfcStepParser.Instance;
|
|
21
|
+
// The middle record carries the bare INF. The records around it are ordinary,
|
|
22
|
+
// and they are the ones the stamping loop used to overwrite - the bug is not
|
|
23
|
+
// in how the INF record itself is read, it is in what reading it does to
|
|
24
|
+
// everything already indexed.
|
|
25
|
+
// The express IDs under test, kept in one place so the assertions read as
|
|
26
|
+
// "the IDs written in the source", which is exactly what the bug destroyed.
|
|
27
|
+
const BEFORE_ID = 11;
|
|
28
|
+
const INF_ID = 22;
|
|
29
|
+
const AFTER_ID = 33;
|
|
30
|
+
const EXPECTED_IDS = [BEFORE_ID, INF_ID, AFTER_ID];
|
|
31
|
+
// One template, one substitution point. Derived with String.replace on a
|
|
32
|
+
// whitespace-exact literal, the controls below would silently become copies of
|
|
33
|
+
// the INF case the moment anyone reflowed the source - replace() returns its
|
|
34
|
+
// input unchanged when the token is absent - and "indexes the same as the
|
|
35
|
+
// equivalent record with a real" would degrade to expect(x).toEqual(x), which
|
|
36
|
+
// passes even with the bug reinstated.
|
|
37
|
+
const VALUE_SLOT = "<<VALUE>>";
|
|
38
|
+
const TEMPLATE = [
|
|
39
|
+
`#${BEFORE_ID}= IFCQUANTITYLENGTH('before',$,$,1.5,$);`,
|
|
40
|
+
`#${INF_ID}= IFCQUANTITYLENGTH('unbounded',$,$,${VALUE_SLOT},$);`,
|
|
41
|
+
`#${AFTER_ID}= IFCQUANTITYLENGTH('after',$,$,2.5,$);`,
|
|
42
|
+
].join("\n");
|
|
43
|
+
/**
|
|
44
|
+
* Build the fixture with a given value in the middle record's 4th attribute.
|
|
45
|
+
*
|
|
46
|
+
* @param value The value text to substitute.
|
|
47
|
+
* @return {string} The fixture source.
|
|
48
|
+
*/
|
|
49
|
+
function withValue(value) {
|
|
50
|
+
if (!TEMPLATE.includes(VALUE_SLOT)) {
|
|
51
|
+
throw new Error("fixture template lost its substitution slot");
|
|
52
|
+
}
|
|
53
|
+
return TEMPLATE.replace(VALUE_SLOT, value);
|
|
54
|
+
}
|
|
55
|
+
// Leading whitespace preserved from the real export, since it is what puts an
|
|
56
|
+
// identifier-like token where an attribute is expected.
|
|
57
|
+
const WITH_INF = withValue(" INF");
|
|
58
|
+
// The control: whatever the index looks like with an ordinary real is what the
|
|
59
|
+
// INF version has to match.
|
|
60
|
+
const WITHOUT_INF = withValue("3.5");
|
|
61
|
+
// The same export writes `-INF` for the opposite bound. Also a CONTROL, not a
|
|
62
|
+
// second case of the bug: a leading '-' is dispatched as a number, so it never
|
|
63
|
+
// reaches the inline-instance recovery and was never affected. Kept so that
|
|
64
|
+
// stays visible - the defect is specific to the bare identifier-like form, and
|
|
65
|
+
// anyone reading INF handling should not assume both directions went through
|
|
66
|
+
// it.
|
|
67
|
+
const WITH_NEGATIVE_INF = withValue(" -INF");
|
|
68
|
+
/**
|
|
69
|
+
* Parse a data block and return each top-level entry's express ID, in order.
|
|
70
|
+
*
|
|
71
|
+
* @param source The STEP text to parse.
|
|
72
|
+
* @return {number[]} Express IDs as indexed.
|
|
73
|
+
*/
|
|
74
|
+
function indexedExpressIDs(source) {
|
|
75
|
+
const input = new ParsingBuffer(new TextEncoder().encode(source));
|
|
76
|
+
const [index, result] = parser.parseDataBlock(input);
|
|
77
|
+
// INCOMPLETE is the expected result for a bare run of records with no
|
|
78
|
+
// ENDSEC; terminator - what matters here is that it is not a syntax error.
|
|
79
|
+
expect([ParseResult.COMPLETE, ParseResult.INCOMPLETE]).toContain(result);
|
|
80
|
+
return index.elements.map((element) => element.expressID);
|
|
81
|
+
}
|
|
82
|
+
describe("a bare INF attribute", () => {
|
|
83
|
+
test("does not stamp its express ID over the records before it", () => {
|
|
84
|
+
expect(indexedExpressIDs(WITH_INF)).toEqual(EXPECTED_IDS);
|
|
85
|
+
});
|
|
86
|
+
test("indexes the same as the equivalent record with a real", () => {
|
|
87
|
+
expect(indexedExpressIDs(WITH_INF))
|
|
88
|
+
.toEqual(indexedExpressIDs(WITHOUT_INF));
|
|
89
|
+
});
|
|
90
|
+
// Passes with or without the fix, by design - see WITH_NEGATIVE_INF above.
|
|
91
|
+
test("negative INF is dispatched as a number and never took this path", () => {
|
|
92
|
+
expect(indexedExpressIDs(WITH_NEGATIVE_INF)).toEqual(EXPECTED_IDS);
|
|
93
|
+
});
|
|
94
|
+
// The whole file has to remain resolvable, not just the index array: a
|
|
95
|
+
// corrupted express ID column makes getElementByExpressID hand back the
|
|
96
|
+
// wrong record, which is how this surfaced as relationship failures rather
|
|
97
|
+
// than as a parse error.
|
|
98
|
+
test("every record is still reachable by its own express ID", () => {
|
|
99
|
+
const [result, model] = parser.parseDataToModel(new ParsingBuffer(new TextEncoder().encode(WITH_INF)));
|
|
100
|
+
expect([ParseResult.COMPLETE, ParseResult.INCOMPLETE]).toContain(result);
|
|
101
|
+
expect(model).not.toBeUndefined();
|
|
102
|
+
for (const expressID of EXPECTED_IDS) {
|
|
103
|
+
const element = model?.getElementByExpressID(expressID);
|
|
104
|
+
expect(element).not.toBeUndefined();
|
|
105
|
+
expect(element?.expressID).toBe(expressID);
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
});
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { StepTypeIndexer } from "../indexing/step_type_indexer.js";
|
|
2
|
+
import { StepEntityConstructorAbstract } from "../step_entity_constructor.js";
|
|
3
|
+
import { ColumnarIndexSink } from "./columnar_index.js";
|
|
4
|
+
/**
|
|
5
|
+
* A type index over the records parsed **so far** (M2, issue #393): the same
|
|
6
|
+
* membership the finished model exposes as `typeIndex`, available while the
|
|
7
|
+
* streaming parse is still running.
|
|
8
|
+
*
|
|
9
|
+
* It is a *derivation over the columnar index*, not a consumer of the
|
|
10
|
+
* per-record event stream — deliberately, and this is the M2 design decision
|
|
11
|
+
* that the 2026-08-18 spike settled (`scripts/m2_consumer_spike.mjs`; issue
|
|
12
|
+
* #393 for the tables). Pushing every record into per-type `Set`s cost +88 %
|
|
13
|
+
* of parse wall-clock and +254 MB on PSB (9.4 M records) and *still* produced
|
|
14
|
+
* the wrong answer — 83 concrete types against the production indexer's 95,
|
|
15
|
+
* because complex records arrive on the event stream as `typeID 0` with their
|
|
16
|
+
* mapped classes stripped. Rebuilding from a prefix snapshot through
|
|
17
|
+
* {@link StepTypeIndexer.createFromColumns} instead costs 121 ms on the same
|
|
18
|
+
* file, sees the complex entries' mapped subtypes, and is membership-identical
|
|
19
|
+
* to the finished model's index by construction — it is literally the same
|
|
20
|
+
* indexer call the model makes.
|
|
21
|
+
*
|
|
22
|
+
* The consequence for callers is that incrementality is a **cadence knob**
|
|
23
|
+
* rather than an architecture. Nothing happens per record; a rebuild happens
|
|
24
|
+
* when someone asks for a view that is more than `growthFactor` stale (14
|
|
25
|
+
* rebuilds over a PSB parse at the default 2.0, +7.2 % if you query
|
|
26
|
+
* continuously, ~0 if you query rarely). Callers that want a specific moment —
|
|
27
|
+
* a preview generation, a progress tick — call {@link refresh} themselves.
|
|
28
|
+
*
|
|
29
|
+
* Two regimes, and the boundary matters: at or below `minimumRecords` a
|
|
30
|
+
* rebuild is trivial, so every query with new records behind it rebuilds and
|
|
31
|
+
* the view is exact; above it, growth pacing applies and a query may see a
|
|
32
|
+
* view up to `growthFactor` stale. The staleness a caller can observe is
|
|
33
|
+
* therefore bounded by the growth factor — never by whatever prefix happened
|
|
34
|
+
* to be current the first time it asked.
|
|
35
|
+
*
|
|
36
|
+
* Queries are only ever answered from a built index, so a caller that has
|
|
37
|
+
* never queried has paid nothing.
|
|
38
|
+
*/
|
|
39
|
+
export declare class PrefixTypeIndex<TypeIDType extends number> {
|
|
40
|
+
private readonly sink_;
|
|
41
|
+
private readonly indexer_;
|
|
42
|
+
private index_;
|
|
43
|
+
private columns_;
|
|
44
|
+
private builtAtRecords_;
|
|
45
|
+
private generation_;
|
|
46
|
+
private readonly growthFactor_;
|
|
47
|
+
private readonly minimumRecords_;
|
|
48
|
+
/**
|
|
49
|
+
* @param sink_ The sink the streaming parse is filling. Held live: each
|
|
50
|
+
* rebuild takes a fresh {@link ColumnarIndexSink.snapshot} of it.
|
|
51
|
+
* @param indexer_ The schema's type indexer — the same instance kind the
|
|
52
|
+
* model builds its own `typeIndex` with, which is what makes the two
|
|
53
|
+
* membership-identical.
|
|
54
|
+
* @param options Rebuild pacing.
|
|
55
|
+
* @param options.growthFactor How much the record count must grow past the
|
|
56
|
+
* last build before a query rebuilds (default 2.0).
|
|
57
|
+
* @param options.minimumRecords The record count below which growth pacing
|
|
58
|
+
* is skipped — small parses just rebuild.
|
|
59
|
+
*/
|
|
60
|
+
constructor(sink_: ColumnarIndexSink<TypeIDType>, indexer_: StepTypeIndexer<TypeIDType>, options?: {
|
|
61
|
+
growthFactor?: number;
|
|
62
|
+
minimumRecords?: number;
|
|
63
|
+
});
|
|
64
|
+
/**
|
|
65
|
+
* How many rebuilds have happened. A caller pacing its own UI can use this
|
|
66
|
+
* to tell a genuinely new view from a repeat query.
|
|
67
|
+
*
|
|
68
|
+
* @return {number} The generation counter, 0 before the first build.
|
|
69
|
+
*/
|
|
70
|
+
get generation(): number;
|
|
71
|
+
/**
|
|
72
|
+
* The record count the current view was built at (0 if never built) — not
|
|
73
|
+
* the parse's current count, which is `sink.count`.
|
|
74
|
+
*
|
|
75
|
+
* @return {number} Records covered by the current view.
|
|
76
|
+
*/
|
|
77
|
+
get recordCount(): number;
|
|
78
|
+
/**
|
|
79
|
+
* Rebuild the view over everything parsed so far, unconditionally. This is
|
|
80
|
+
* the expensive call — O(records) — and it is the caller's choice of when.
|
|
81
|
+
*/
|
|
82
|
+
refresh(): void;
|
|
83
|
+
/**
|
|
84
|
+
* Rebuild only if the parse has moved far enough past the current view (or
|
|
85
|
+
* there is no view yet). Every query goes through this.
|
|
86
|
+
*/
|
|
87
|
+
private ensureFresh;
|
|
88
|
+
/**
|
|
89
|
+
* The distinct types present in the current view, including the mapped
|
|
90
|
+
* classes of complex entries.
|
|
91
|
+
*
|
|
92
|
+
* @return {IterableIterator<TypeIDType>} The types.
|
|
93
|
+
*/
|
|
94
|
+
types(): IterableIterator<TypeIDType>;
|
|
95
|
+
/**
|
|
96
|
+
* Express IDs of every record of the given types (subtype closures unioned
|
|
97
|
+
* via the generated `query`, conway #383) among the records parsed so far.
|
|
98
|
+
*
|
|
99
|
+
* @param types The entity constructors to query.
|
|
100
|
+
* @return {IterableIterator<number>} Matching express IDs, in parse order.
|
|
101
|
+
* @yields {number} Each matching express ID.
|
|
102
|
+
*/
|
|
103
|
+
expressIDsOfTypes(...types: StepEntityConstructorAbstract<TypeIDType>[]): IterableIterator<number>;
|
|
104
|
+
/**
|
|
105
|
+
* Express IDs of records whose concrete (or mapped) type is one of the
|
|
106
|
+
* given raw type IDs — no subtype expansion. The closure-expanding
|
|
107
|
+
* {@link expressIDsOfTypes} is the caller-facing form; this is for callers
|
|
108
|
+
* that already hold type IDs.
|
|
109
|
+
*
|
|
110
|
+
* @param typeIDs The raw type IDs to union.
|
|
111
|
+
* @return {IterableIterator<number>} Matching express IDs, in parse order.
|
|
112
|
+
* @yields {number} Each matching express ID.
|
|
113
|
+
*/
|
|
114
|
+
expressIDsOfTypeIDs(...typeIDs: TypeIDType[]): IterableIterator<number>;
|
|
115
|
+
/**
|
|
116
|
+
* Upper-bound count of records of the given types in the current view (see
|
|
117
|
+
* {@link MultiIndexSet.count} for why it is an upper bound).
|
|
118
|
+
*
|
|
119
|
+
* @param types The entity constructors to count.
|
|
120
|
+
* @return {number} The count.
|
|
121
|
+
*/
|
|
122
|
+
count(...types: StepEntityConstructorAbstract<TypeIDType>[]): number;
|
|
123
|
+
}
|
|
124
|
+
//# sourceMappingURL=prefix_type_index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prefix_type_index.d.ts","sourceRoot":"","sources":["../../../../src/step/parsing/prefix_type_index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAA;AAC/D,OAAO,EAAE,6BAA6B,EAAE,MAAM,4BAA4B,CAAA;AAC1E,OAAO,EAAE,iBAAiB,EAAoB,MAAM,kBAAkB,CAAA;AAUtE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,qBAAa,eAAe,CAAC,UAAU,SAAS,MAAM;IA2BlD,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IA1B3B,OAAO,CAAC,MAAM,CAAgD;IAE9D,OAAO,CAAC,QAAQ,CAAmD;IAEnE,OAAO,CAAC,eAAe,CAAI;IAE3B,OAAO,CAAC,WAAW,CAAI;IAEvB,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAQ;IAEtC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAQ;IAExC;;;;;;;;;;;OAWG;gBAEgB,KAAK,EAAE,iBAAiB,CAAC,UAAU,CAAC,EACpC,QAAQ,EAAE,eAAe,CAAC,UAAU,CAAC,EACtD,OAAO,CAAC,EAAE;QAAE,YAAY,CAAC,EAAE,MAAM,CAAC;QAAC,cAAc,CAAC,EAAE,MAAM,CAAA;KAAE;IAM9D;;;;;OAKG;IACH,IAAW,UAAU,IAAI,MAAM,CAE9B;IAED;;;;;OAKG;IACH,IAAW,WAAW,IAAI,MAAM,CAE/B;IAED;;;OAGG;IACI,OAAO,IAAI,IAAI;IAOtB;;;OAGG;IACH,OAAO,CAAC,WAAW;IAqCnB;;;;;OAKG;IACI,KAAK,IAAI,gBAAgB,CAAC,UAAU,CAAC;IAM5C;;;;;;;OAOG;IACK,iBAAiB,CACrB,GAAG,KAAK,EAAE,6BAA6B,CAAC,UAAU,CAAC,EAAE,GACrD,gBAAgB,CAAC,MAAM,CAAC;IAS5B;;;;;;;;;OASG;IACK,mBAAmB,CAAE,GAAG,OAAO,EAAE,UAAU,EAAE,GAAI,gBAAgB,CAAC,MAAM,CAAC;IAuCjF;;;;;;OAMG;IACI,KAAK,CAAE,GAAG,KAAK,EAAE,6BAA6B,CAAC,UAAU,CAAC,EAAE,GAAI,MAAM;CAS9E"}
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import { extractOneHotLow } from "../../indexing/bit_operations.js";
|
|
2
|
+
/** Default record growth between automatic rebuilds — see the class docs. */
|
|
3
|
+
const DEFAULT_GROWTH_FACTOR = 2;
|
|
4
|
+
/** Below this many records a rebuild is cheap enough not to bother pacing. */
|
|
5
|
+
const DEFAULT_MINIMUM_RECORDS = 1024;
|
|
6
|
+
/**
|
|
7
|
+
* A type index over the records parsed **so far** (M2, issue #393): the same
|
|
8
|
+
* membership the finished model exposes as `typeIndex`, available while the
|
|
9
|
+
* streaming parse is still running.
|
|
10
|
+
*
|
|
11
|
+
* It is a *derivation over the columnar index*, not a consumer of the
|
|
12
|
+
* per-record event stream — deliberately, and this is the M2 design decision
|
|
13
|
+
* that the 2026-08-18 spike settled (`scripts/m2_consumer_spike.mjs`; issue
|
|
14
|
+
* #393 for the tables). Pushing every record into per-type `Set`s cost +88 %
|
|
15
|
+
* of parse wall-clock and +254 MB on PSB (9.4 M records) and *still* produced
|
|
16
|
+
* the wrong answer — 83 concrete types against the production indexer's 95,
|
|
17
|
+
* because complex records arrive on the event stream as `typeID 0` with their
|
|
18
|
+
* mapped classes stripped. Rebuilding from a prefix snapshot through
|
|
19
|
+
* {@link StepTypeIndexer.createFromColumns} instead costs 121 ms on the same
|
|
20
|
+
* file, sees the complex entries' mapped subtypes, and is membership-identical
|
|
21
|
+
* to the finished model's index by construction — it is literally the same
|
|
22
|
+
* indexer call the model makes.
|
|
23
|
+
*
|
|
24
|
+
* The consequence for callers is that incrementality is a **cadence knob**
|
|
25
|
+
* rather than an architecture. Nothing happens per record; a rebuild happens
|
|
26
|
+
* when someone asks for a view that is more than `growthFactor` stale (14
|
|
27
|
+
* rebuilds over a PSB parse at the default 2.0, +7.2 % if you query
|
|
28
|
+
* continuously, ~0 if you query rarely). Callers that want a specific moment —
|
|
29
|
+
* a preview generation, a progress tick — call {@link refresh} themselves.
|
|
30
|
+
*
|
|
31
|
+
* Two regimes, and the boundary matters: at or below `minimumRecords` a
|
|
32
|
+
* rebuild is trivial, so every query with new records behind it rebuilds and
|
|
33
|
+
* the view is exact; above it, growth pacing applies and a query may see a
|
|
34
|
+
* view up to `growthFactor` stale. The staleness a caller can observe is
|
|
35
|
+
* therefore bounded by the growth factor — never by whatever prefix happened
|
|
36
|
+
* to be current the first time it asked.
|
|
37
|
+
*
|
|
38
|
+
* Queries are only ever answered from a built index, so a caller that has
|
|
39
|
+
* never queried has paid nothing.
|
|
40
|
+
*/
|
|
41
|
+
export class PrefixTypeIndex {
|
|
42
|
+
/**
|
|
43
|
+
* @param sink_ The sink the streaming parse is filling. Held live: each
|
|
44
|
+
* rebuild takes a fresh {@link ColumnarIndexSink.snapshot} of it.
|
|
45
|
+
* @param indexer_ The schema's type indexer — the same instance kind the
|
|
46
|
+
* model builds its own `typeIndex` with, which is what makes the two
|
|
47
|
+
* membership-identical.
|
|
48
|
+
* @param options Rebuild pacing.
|
|
49
|
+
* @param options.growthFactor How much the record count must grow past the
|
|
50
|
+
* last build before a query rebuilds (default 2.0).
|
|
51
|
+
* @param options.minimumRecords The record count below which growth pacing
|
|
52
|
+
* is skipped — small parses just rebuild.
|
|
53
|
+
*/
|
|
54
|
+
constructor(sink_, indexer_, options) {
|
|
55
|
+
this.sink_ = sink_;
|
|
56
|
+
this.indexer_ = indexer_;
|
|
57
|
+
this.index_ = void 0;
|
|
58
|
+
this.columns_ = void 0;
|
|
59
|
+
this.builtAtRecords_ = 0;
|
|
60
|
+
this.generation_ = 0;
|
|
61
|
+
this.growthFactor_ = options?.growthFactor ?? DEFAULT_GROWTH_FACTOR;
|
|
62
|
+
this.minimumRecords_ = options?.minimumRecords ?? DEFAULT_MINIMUM_RECORDS;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* How many rebuilds have happened. A caller pacing its own UI can use this
|
|
66
|
+
* to tell a genuinely new view from a repeat query.
|
|
67
|
+
*
|
|
68
|
+
* @return {number} The generation counter, 0 before the first build.
|
|
69
|
+
*/
|
|
70
|
+
get generation() {
|
|
71
|
+
return this.generation_;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* The record count the current view was built at (0 if never built) — not
|
|
75
|
+
* the parse's current count, which is `sink.count`.
|
|
76
|
+
*
|
|
77
|
+
* @return {number} Records covered by the current view.
|
|
78
|
+
*/
|
|
79
|
+
get recordCount() {
|
|
80
|
+
return this.builtAtRecords_;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Rebuild the view over everything parsed so far, unconditionally. This is
|
|
84
|
+
* the expensive call — O(records) — and it is the caller's choice of when.
|
|
85
|
+
*/
|
|
86
|
+
refresh() {
|
|
87
|
+
this.columns_ = this.sink_.snapshot();
|
|
88
|
+
this.index_ = this.indexer_.createFromColumns(this.columns_);
|
|
89
|
+
this.builtAtRecords_ = this.sink_.topLevelCount;
|
|
90
|
+
++this.generation_;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Rebuild only if the parse has moved far enough past the current view (or
|
|
94
|
+
* there is no view yet). Every query goes through this.
|
|
95
|
+
*/
|
|
96
|
+
ensureFresh() {
|
|
97
|
+
const parsed = this.sink_.topLevelCount;
|
|
98
|
+
if (this.index_ === void 0) {
|
|
99
|
+
this.refresh();
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
// A count that went BACKWARDS means the sink was reset — the streaming
|
|
103
|
+
// builder's grow-and-restart. The current view then describes records the
|
|
104
|
+
// restarted parse has not reached again, and growth pacing would never
|
|
105
|
+
// catch it (the new parse may finish below the old threshold), so it is
|
|
106
|
+
// rebuilt immediately rather than paced.
|
|
107
|
+
if (parsed < this.builtAtRecords_) {
|
|
108
|
+
this.refresh();
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
if (parsed === this.builtAtRecords_) {
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
// Below the minimum a rebuild is trivial, so the view is kept exact:
|
|
115
|
+
// pacing here would buy nothing, and — because the threshold is absolute
|
|
116
|
+
// rather than relative to the last build — it would freeze a small file's
|
|
117
|
+
// view at whatever prefix happened to be current when it was first
|
|
118
|
+
// queried, permanently, including after the parse finishes.
|
|
119
|
+
if (parsed <= this.minimumRecords_) {
|
|
120
|
+
this.refresh();
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
if (parsed >= this.builtAtRecords_ * this.growthFactor_) {
|
|
124
|
+
this.refresh();
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* The distinct types present in the current view, including the mapped
|
|
129
|
+
* classes of complex entries.
|
|
130
|
+
*
|
|
131
|
+
* @return {IterableIterator<TypeIDType>} The types.
|
|
132
|
+
*/
|
|
133
|
+
types() {
|
|
134
|
+
this.ensureFresh();
|
|
135
|
+
return this.index_.types();
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Express IDs of every record of the given types (subtype closures unioned
|
|
139
|
+
* via the generated `query`, conway #383) among the records parsed so far.
|
|
140
|
+
*
|
|
141
|
+
* @param types The entity constructors to query.
|
|
142
|
+
* @return {IterableIterator<number>} Matching express IDs, in parse order.
|
|
143
|
+
* @yields {number} Each matching express ID.
|
|
144
|
+
*/
|
|
145
|
+
*expressIDsOfTypes(...types) {
|
|
146
|
+
const distinct = types.length === 1 ?
|
|
147
|
+
types[0].query :
|
|
148
|
+
new Set(types.flatMap((type) => type.query));
|
|
149
|
+
yield* this.expressIDsOfTypeIDs(...distinct);
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Express IDs of records whose concrete (or mapped) type is one of the
|
|
153
|
+
* given raw type IDs — no subtype expansion. The closure-expanding
|
|
154
|
+
* {@link expressIDsOfTypes} is the caller-facing form; this is for callers
|
|
155
|
+
* that already hold type IDs.
|
|
156
|
+
*
|
|
157
|
+
* @param typeIDs The raw type IDs to union.
|
|
158
|
+
* @return {IterableIterator<number>} Matching express IDs, in parse order.
|
|
159
|
+
* @yields {number} Each matching express ID.
|
|
160
|
+
*/
|
|
161
|
+
*expressIDsOfTypeIDs(...typeIDs) {
|
|
162
|
+
this.ensureFresh();
|
|
163
|
+
const columns = this.columns_;
|
|
164
|
+
const expressIDs = columns.expressID;
|
|
165
|
+
const firstInlineElement = columns.firstInlineElement;
|
|
166
|
+
const cursor = this.index_.cursor(...typeIDs);
|
|
167
|
+
// Cursors are pooled, and a generator can be abandoned mid-iteration (a
|
|
168
|
+
// `break`, a `find`), so the release has to survive early exit — hence
|
|
169
|
+
// try/finally rather than a free() after the loop. Mirrors
|
|
170
|
+
// StepModelBase.expressIDsOfTypes, which this is the prefix twin of; a
|
|
171
|
+
// live consumer querying every generation would otherwise churn a cursor
|
|
172
|
+
// per query for the pool's whole lifetime.
|
|
173
|
+
try {
|
|
174
|
+
while (cursor.step()) {
|
|
175
|
+
const high = cursor.high;
|
|
176
|
+
let low = cursor.low;
|
|
177
|
+
while (low !== 0) {
|
|
178
|
+
const lowestOneHot = extractOneHotLow(low);
|
|
179
|
+
low ^= (1 << lowestOneHot);
|
|
180
|
+
const localID = high | lowestOneHot;
|
|
181
|
+
// Inline entities carry no express ID — the same guard the model's
|
|
182
|
+
// expressIDsOfTypes applies over its own columns.
|
|
183
|
+
if (localID < firstInlineElement) {
|
|
184
|
+
yield expressIDs[localID];
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
finally {
|
|
190
|
+
cursor.free();
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Upper-bound count of records of the given types in the current view (see
|
|
195
|
+
* {@link MultiIndexSet.count} for why it is an upper bound).
|
|
196
|
+
*
|
|
197
|
+
* @param types The entity constructors to count.
|
|
198
|
+
* @return {number} The count.
|
|
199
|
+
*/
|
|
200
|
+
count(...types) {
|
|
201
|
+
this.ensureFresh();
|
|
202
|
+
const distinct = types.length === 1 ?
|
|
203
|
+
types[0].query :
|
|
204
|
+
[...new Set(types.flatMap((type) => type.query))];
|
|
205
|
+
return this.index_.count(...distinct);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prefix_type_index.test.d.ts","sourceRoot":"","sources":["../../../../src/step/parsing/prefix_type_index.test.ts"],"names":[],"mappings":""}
|