@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
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import StepParser from "../step/parsing/step_parser.js";
|
|
2
2
|
import EntitTypesIfcSearch from "./ifc4_gen/entity_types_search.gen.js";
|
|
3
3
|
import IfcStepModel from "./ifc_step_model.js";
|
|
4
|
+
import { buildColumnarIndexStreaming, buildColumnarIndexStreamingAsync, } from "../step/parsing/streaming_index_builder.js";
|
|
5
|
+
import { WindowedStepBufferProvider, } from "../step/step_buffer_provider.js";
|
|
6
|
+
/** Default moving-window size for the streaming index build (1 MiB). */
|
|
7
|
+
// eslint-disable-next-line no-magic-numbers
|
|
8
|
+
const DEFAULT_STREAM_POOL_BYTES = 1024 * 1024;
|
|
4
9
|
/**
|
|
5
10
|
* Parser for taking IFC file serialized in step and turning them into a lazily parsed model.
|
|
6
11
|
*/
|
|
@@ -36,6 +41,65 @@ class IfcStepParser extends StepParser {
|
|
|
36
41
|
const [itemIndex, parseResult] = await this.parseDataBlockAsync(input, onProgress);
|
|
37
42
|
return [parseResult, new IfcStepModel(input.buffer, itemIndex.elements)];
|
|
38
43
|
}
|
|
44
|
+
/**
|
|
45
|
+
* Build a model by streaming the source through a bounded moving window
|
|
46
|
+
* (see buildIndexStreaming / M0) rather than parsing one resident buffer,
|
|
47
|
+
* then backing the model with a windowed provider over `store` — so the
|
|
48
|
+
* source is never held fully resident in the JS heap.
|
|
49
|
+
*
|
|
50
|
+
* `source` serves the parse (synchronous windowed reads — on a worker this
|
|
51
|
+
* is an OPFS sync-access handle; in node/tests a file descriptor or buffer)
|
|
52
|
+
* and `store` serves the model's post-parse property access (asynchronous
|
|
53
|
+
* windowed reads paged in on demand — OPFS `File.slice()` in the browser).
|
|
54
|
+
* Both view the same file bytes, so the file-absolute addresses the index
|
|
55
|
+
* records resolve identically through either.
|
|
56
|
+
*
|
|
57
|
+
* NOTE (M1 scope): this delivers the bounded-memory *parse*. Synchronous
|
|
58
|
+
* geometry extraction still needs its record ranges resident — as after
|
|
59
|
+
* `spillSourceToExternalStore` — so a caller that extracts geometry must
|
|
60
|
+
* `ensureResident` first (demand-driven geometry is M3). Property / index
|
|
61
|
+
* access works directly via the async surfaces.
|
|
62
|
+
*
|
|
63
|
+
* @param source Synchronous byte source feeding the streaming parse.
|
|
64
|
+
* @param store Async external store backing the windowed model.
|
|
65
|
+
* @param opts Optional window sizing: `pool` (parse window),
|
|
66
|
+
* `chunkBytes` / `maxResidentChunks` (model window).
|
|
67
|
+
* @return {[ParseResult, IfcStepModel | undefined]} The parse result and
|
|
68
|
+
* the windowed model.
|
|
69
|
+
*/
|
|
70
|
+
parseStreamToModel(source, store, opts) {
|
|
71
|
+
if (store.byteLength !== source.byteLength) {
|
|
72
|
+
throw new Error(`Streaming store byteLength ${store.byteLength} does not match ` +
|
|
73
|
+
`source byteLength ${source.byteLength}`);
|
|
74
|
+
}
|
|
75
|
+
// Columnar build (M7): the index goes straight into SoA columns — the
|
|
76
|
+
// per-record object phase never exists, so peak heap is window + columns.
|
|
77
|
+
const { columns, result } = buildColumnarIndexStreaming(source, this, opts?.pool ?? DEFAULT_STREAM_POOL_BYTES);
|
|
78
|
+
const provider = new WindowedStepBufferProvider(store, opts?.chunkBytes, opts?.maxResidentChunks);
|
|
79
|
+
return [result, new IfcStepModel(void 0, columns, provider)];
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Cooperative twin of {@link parseStreamToModel}: the index build
|
|
83
|
+
* yields to the event loop and can fill windows from an async
|
|
84
|
+
* {@link ReadableByteSource} (an OPFS `File.slice()` store). The
|
|
85
|
+
* model is still windowed — geometry extract must
|
|
86
|
+
* `ensureResident` first.
|
|
87
|
+
*
|
|
88
|
+
* @param source Sync or async byte source feeding the parse.
|
|
89
|
+
* @param store Async external store backing the windowed model.
|
|
90
|
+
* @param opts Optional window sizing plus parse progress.
|
|
91
|
+
* @return {Promise<[ParseResult, IfcStepModel | undefined]>} The
|
|
92
|
+
* parse result and the windowed model.
|
|
93
|
+
*/
|
|
94
|
+
async parseStreamToModelAsync(source, store, opts) {
|
|
95
|
+
if (store.byteLength !== source.byteLength) {
|
|
96
|
+
throw new Error(`Streaming store byteLength ${store.byteLength} does not match ` +
|
|
97
|
+
`source byteLength ${source.byteLength}`);
|
|
98
|
+
}
|
|
99
|
+
const { columns, result } = await buildColumnarIndexStreamingAsync(source, this, opts?.pool ?? DEFAULT_STREAM_POOL_BYTES, void 0, opts?.onProgress);
|
|
100
|
+
const provider = new WindowedStepBufferProvider(store, opts?.chunkBytes, opts?.maxResidentChunks);
|
|
101
|
+
return [result, new IfcStepModel(void 0, columns, provider)];
|
|
102
|
+
}
|
|
39
103
|
}
|
|
40
104
|
/**
|
|
41
105
|
* An easily accessible and re-usable instance of the parser.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ifc_stream_model.test.d.ts","sourceRoot":"","sources":["../../../src/ifc/ifc_stream_model.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/* eslint-disable no-magic-numbers */
|
|
2
|
+
// M1 core: a model built by streaming the source through a bounded window
|
|
3
|
+
// (parseStreamToModel) must decode records byte-identically to the resident
|
|
4
|
+
// parse — reading them on demand through the windowed provider after
|
|
5
|
+
// ensureResident, with the source never held fully resident.
|
|
6
|
+
import * as fs from "fs";
|
|
7
|
+
import { beforeAll, describe, expect, test } from "@jest/globals";
|
|
8
|
+
import ParsingBuffer from "../parsing/parsing_buffer.js";
|
|
9
|
+
import IfcStepParser from "./ifc_step_parser.js";
|
|
10
|
+
import { BufferByteSource } from "../step/parsing/byte_source.js";
|
|
11
|
+
import { InMemoryStepByteStore } from "../step/step_buffer_provider.js";
|
|
12
|
+
import { ParseResult } from "../step/parsing/step_parser.js";
|
|
13
|
+
import { IfcRoot } from "./ifc4_gen/index.js";
|
|
14
|
+
let bytes;
|
|
15
|
+
/**
|
|
16
|
+
* Decode every IfcRoot-derived entity's GlobalId + Name from a model into a
|
|
17
|
+
* stable map — the attributes are read from the record bytes, so this
|
|
18
|
+
* exercises the actual byte path (resident buffer vs windowed provider).
|
|
19
|
+
*
|
|
20
|
+
* @param model The model to read from.
|
|
21
|
+
* @return {Map<number, string>} expressID → "GlobalId|Name".
|
|
22
|
+
*/
|
|
23
|
+
function rootAttributes(model) {
|
|
24
|
+
const out = new Map();
|
|
25
|
+
for (const expressID of model.expressIDsOfTypes(IfcRoot)) {
|
|
26
|
+
const entity = model.getElementByExpressID(expressID);
|
|
27
|
+
if (entity === void 0) {
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
const globalId = typeof entity.GlobalId === "string" ? entity.GlobalId : "";
|
|
31
|
+
const name = typeof entity.Name === "string" ? entity.Name : "";
|
|
32
|
+
out.set(expressID, `${globalId}|${name}`);
|
|
33
|
+
}
|
|
34
|
+
return out;
|
|
35
|
+
}
|
|
36
|
+
beforeAll(() => {
|
|
37
|
+
bytes = new Uint8Array(fs.readFileSync("data/index.ifc"));
|
|
38
|
+
});
|
|
39
|
+
describe("parseStreamToModel", () => {
|
|
40
|
+
test("streamed windowed model decodes records identically to the resident parse", async () => {
|
|
41
|
+
// Resident ground truth.
|
|
42
|
+
const residentInput = new ParsingBuffer(bytes);
|
|
43
|
+
IfcStepParser.Instance.parseHeader(residentInput);
|
|
44
|
+
const [, residentModel] = IfcStepParser.Instance.parseDataToModel(residentInput);
|
|
45
|
+
const expected = rootAttributes(residentModel);
|
|
46
|
+
expect(expected.size).toBeGreaterThan(10);
|
|
47
|
+
// Streamed: tiny parse window forces slides; tiny model chunks force
|
|
48
|
+
// paging. Same bytes behind both the sync source and the async store.
|
|
49
|
+
const [result, streamModel] = IfcStepParser.Instance.parseStreamToModel(new BufferByteSource(bytes), new InMemoryStepByteStore(bytes), { pool: 4 * 1024, chunkBytes: 512, maxResidentChunks: 3 });
|
|
50
|
+
expect(result).toBe(ParseResult.COMPLETE);
|
|
51
|
+
expect(streamModel.isSourceExternal).toBe(true);
|
|
52
|
+
// Page each record in on demand, then decode; must match resident.
|
|
53
|
+
const actual = new Map();
|
|
54
|
+
for (const expressID of streamModel.expressIDsOfTypes(IfcRoot)) {
|
|
55
|
+
await streamModel.ensureResidentByExpressID(expressID);
|
|
56
|
+
const entity = streamModel.getElementByExpressID(expressID);
|
|
57
|
+
if (entity === void 0) {
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
const globalId = typeof entity.GlobalId === "string" ? entity.GlobalId : "";
|
|
61
|
+
const name = typeof entity.Name === "string" ? entity.Name : "";
|
|
62
|
+
actual.set(expressID, `${globalId}|${name}`);
|
|
63
|
+
}
|
|
64
|
+
expect(actual).toEqual(expected);
|
|
65
|
+
}, 60000);
|
|
66
|
+
test("rejects a store whose size does not match the source", () => {
|
|
67
|
+
expect(() => IfcStepParser.Instance.parseStreamToModel(new BufferByteSource(bytes), new InMemoryStepByteStore(bytes.subarray(0, bytes.length - 1))))
|
|
68
|
+
.toThrow(/does not match/);
|
|
69
|
+
});
|
|
70
|
+
});
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { ByteSource, ReadableByteSource } from "../step/parsing/byte_source.js";
|
|
2
|
+
import { ColumnarIndexSink, StepIndexColumns } from "../step/parsing/columnar_index.js";
|
|
3
|
+
import { RecordEventHandler } from "../step/parsing/record_event.js";
|
|
4
|
+
import { StreamingIndexStats } from "../step/parsing/streaming_index_builder.js";
|
|
5
|
+
import { ParseResult, StepHeader } from "../step/parsing/step_parser.js";
|
|
6
|
+
import { StepExternalByteStore } from "../step/step_buffer_provider.js";
|
|
7
|
+
import EntityTypesIfc from "./ifc4_gen/entity_types_ifc.gen.js";
|
|
8
|
+
import { PrefixTypeIndex } from "../step/parsing/prefix_type_index.js";
|
|
9
|
+
import IfcStepModel from "./ifc_step_model.js";
|
|
10
|
+
/**
|
|
11
|
+
* Options for a streamed IFC open. All optional; defaults suit a browser
|
|
12
|
+
* worker over an OPFS-backed store.
|
|
13
|
+
*/
|
|
14
|
+
export interface StreamedIfcOpenOptions {
|
|
15
|
+
/** Parse window size in bytes (default 1 MiB). */
|
|
16
|
+
pool?: number;
|
|
17
|
+
/** Windowed provider chunk size (default: provider's). */
|
|
18
|
+
chunkBytes?: number;
|
|
19
|
+
/** Windowed provider LRU cap in chunks (default: provider's). */
|
|
20
|
+
maxResidentChunks?: number;
|
|
21
|
+
/**
|
|
22
|
+
* Live per-record event — the M2 seam for incremental consumers (type
|
|
23
|
+
* index, roots, cross-refs, names skeleton) that run while the model is
|
|
24
|
+
* still parsing. Must be synchronous and cheap; see
|
|
25
|
+
* {@link RecordEventHandler} for the payload and the window-lifetime rule
|
|
26
|
+
* on the record bytes it hands over.
|
|
27
|
+
*/
|
|
28
|
+
onRecordIndexed?: RecordEventHandler<EntityTypesIfc>;
|
|
29
|
+
/**
|
|
30
|
+
* Caller-owned index sink. Pass one to hold the columns *while* the parse
|
|
31
|
+
* runs — the seam a prefix-derived consumer reads
|
|
32
|
+
* ({@link ifcPrefixTypeIndex}). Without it the columns only exist once the
|
|
33
|
+
* open returns, which is too late for anything mid-parse.
|
|
34
|
+
*/
|
|
35
|
+
indexSink?: ColumnarIndexSink<EntityTypesIfc>;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* The result of a streamed IFC open: the windowed-source model plus the
|
|
39
|
+
* artifacts a consumer needs around it.
|
|
40
|
+
*/
|
|
41
|
+
export interface StreamedIfcOpen {
|
|
42
|
+
/** The model over the windowed source (undefined if the parse failed). */
|
|
43
|
+
model: IfcStepModel | undefined;
|
|
44
|
+
/** The parse result. */
|
|
45
|
+
result: ParseResult;
|
|
46
|
+
/** The STEP header (available even on some failed parses). */
|
|
47
|
+
header: StepHeader;
|
|
48
|
+
/**
|
|
49
|
+
* The columnar index the model was built from. Hand to
|
|
50
|
+
* `serializeIndexSidecarFromColumns` (with a source hash) to produce the
|
|
51
|
+
* revisit sidecar — the columns ARE the sidecar payload (M7 identity).
|
|
52
|
+
*/
|
|
53
|
+
columns: StepIndexColumns<EntityTypesIfc>;
|
|
54
|
+
/** Window diagnostics from the streaming build. */
|
|
55
|
+
stats: StreamingIndexStats;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Open an IFC model from a streamed source with a **fixed-memory parse**
|
|
59
|
+
* (the release-facing Phase B API; composes M0/M1a/M7):
|
|
60
|
+
*
|
|
61
|
+
* - the index builds through a moving window over `source` — the source is
|
|
62
|
+
* never resident in the JS heap, and the index is columnar from birth
|
|
63
|
+
* (no per-record object phase);
|
|
64
|
+
* - the model reads source bytes on demand through a windowed LRU provider
|
|
65
|
+
* over `store` (`ensureResidentByLocalID`/`ByExpressID` page ranges in
|
|
66
|
+
* before synchronous reads — see `DemandResidencyPump` for the demand
|
|
67
|
+
* orchestration).
|
|
68
|
+
*
|
|
69
|
+
* The typical browser wiring: a worker streams the network body **through**
|
|
70
|
+
* to OPFS while feeding the same bytes to `source`; `store` reads back from
|
|
71
|
+
* OPFS. `source.byteLength` and `store.byteLength` must agree.
|
|
72
|
+
*
|
|
73
|
+
* @param source The sequential parse source.
|
|
74
|
+
* @param store The random-access store the model reads from afterwards.
|
|
75
|
+
* @param options See {@link StreamedIfcOpenOptions}.
|
|
76
|
+
* @return {StreamedIfcOpen} The model + header, columns, and diagnostics.
|
|
77
|
+
*/
|
|
78
|
+
export declare function openStreamedIfcModel(source: ByteSource, store: StepExternalByteStore, options?: StreamedIfcOpenOptions): StreamedIfcOpen;
|
|
79
|
+
/**
|
|
80
|
+
* A type index over the IFC records parsed so far, paired with the schema's
|
|
81
|
+
* production indexer — the composed form of {@link PrefixTypeIndex} that a
|
|
82
|
+
* caller needs, since the index is only useful mid-parse if it reads the same
|
|
83
|
+
* live sink the open is filling.
|
|
84
|
+
*
|
|
85
|
+
* ```ts
|
|
86
|
+
* const indexSink = new ColumnarIndexSink<EntityTypesIfc>()
|
|
87
|
+
* const types = ifcPrefixTypeIndex( indexSink )
|
|
88
|
+
* const open = await openStreamedIfcModelAsync( source, store, {
|
|
89
|
+
* indexSink,
|
|
90
|
+
* onProgress: () => reveal( types.expressIDsOfTypes( IfcProduct ) ),
|
|
91
|
+
* } )
|
|
92
|
+
* ```
|
|
93
|
+
*
|
|
94
|
+
* @param sink The sink passed to the open as `indexSink`.
|
|
95
|
+
* @param options Rebuild pacing (see {@link PrefixTypeIndex}).
|
|
96
|
+
* @param options.growthFactor Record growth before a query rebuilds.
|
|
97
|
+
* @param options.minimumRecords Count below which pacing is skipped.
|
|
98
|
+
* @return {PrefixTypeIndex<EntityTypesIfc>} The index over that sink.
|
|
99
|
+
*/
|
|
100
|
+
export declare function ifcPrefixTypeIndex(sink: ColumnarIndexSink<EntityTypesIfc>, options?: {
|
|
101
|
+
growthFactor?: number;
|
|
102
|
+
minimumRecords?: number;
|
|
103
|
+
}): PrefixTypeIndex<EntityTypesIfc>;
|
|
104
|
+
/**
|
|
105
|
+
* Cooperative twin of {@link openStreamedIfcModel}: the index build
|
|
106
|
+
* yields to the event loop and can fill windows from an async
|
|
107
|
+
* {@link ReadableByteSource}. Pass a {@link StoreByteSource} wrapping
|
|
108
|
+
* `store` to parse a file that is already in OPFS without ever
|
|
109
|
+
* holding it as one `ArrayBuffer`.
|
|
110
|
+
*
|
|
111
|
+
* @param source Sync or async parse source.
|
|
112
|
+
* @param store The random-access store the model reads from afterwards.
|
|
113
|
+
* @param options See {@link StreamedIfcOpenOptions} plus `onProgress`.
|
|
114
|
+
* @return {Promise<StreamedIfcOpen>} The model + header, columns, diagnostics.
|
|
115
|
+
*/
|
|
116
|
+
export declare function openStreamedIfcModelAsync(source: ReadableByteSource, store: StepExternalByteStore, options?: StreamedIfcOpenOptions & {
|
|
117
|
+
onProgress?: (absoluteByteCursor: number) => void;
|
|
118
|
+
}): Promise<StreamedIfcOpen>;
|
|
119
|
+
/**
|
|
120
|
+
* Open a windowed IFC model from a store alone — parse windows and
|
|
121
|
+
* post-parse reads share `store`. This is the M1b embedder entry for
|
|
122
|
+
* a file already sitting in OPFS.
|
|
123
|
+
*
|
|
124
|
+
* @param store The source of truth for the file bytes.
|
|
125
|
+
* @param options See {@link openStreamedIfcModelAsync}.
|
|
126
|
+
* @return {Promise<StreamedIfcOpen>} The model + header, columns, diagnostics.
|
|
127
|
+
*/
|
|
128
|
+
export declare function openStreamedIfcModelFromStore(store: StepExternalByteStore, options?: StreamedIfcOpenOptions & {
|
|
129
|
+
onProgress?: (absoluteByteCursor: number) => void;
|
|
130
|
+
}): Promise<StreamedIfcOpen>;
|
|
131
|
+
//# sourceMappingURL=ifc_stream_open.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ifc_stream_open.d.ts","sourceRoot":"","sources":["../../../src/ifc/ifc_stream_open.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,kBAAkB,EAAmB,MAAM,6BAA6B,CAAA;AAC7F,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAA;AACpF,OAAO,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAA;AACjE,OAAO,EAGL,mBAAmB,EACpB,MAAM,yCAAyC,CAAA;AAChD,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAA;AACrE,OAAO,EACL,qBAAqB,EAEtB,MAAM,8BAA8B,CAAA;AACrC,OAAO,cAAuC,MAAM,iCAAiC,CAAA;AACrF,OAAO,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAA;AAEnE,OAAO,YAAY,MAAM,kBAAkB,CAAA;AAI3C;;;GAGG;AACH,MAAM,WAAW,sBAAsB;IAErC,kDAAkD;IAClD,IAAI,CAAC,EAAE,MAAM,CAAA;IAEb,0DAA0D;IAC1D,UAAU,CAAC,EAAE,MAAM,CAAA;IAEnB,iEAAiE;IACjE,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAE1B;;;;;;OAMG;IACH,eAAe,CAAC,EAAE,kBAAkB,CAAC,cAAc,CAAC,CAAA;IAEpD;;;;;OAKG;IACH,SAAS,CAAC,EAAE,iBAAiB,CAAC,cAAc,CAAC,CAAA;CAC9C;AAGD;;;GAGG;AACH,MAAM,WAAW,eAAe;IAE9B,0EAA0E;IAC1E,KAAK,EAAE,YAAY,GAAG,SAAS,CAAA;IAE/B,wBAAwB;IACxB,MAAM,EAAE,WAAW,CAAA;IAEnB,8DAA8D;IAC9D,MAAM,EAAE,UAAU,CAAA;IAElB;;;;OAIG;IACH,OAAO,EAAE,gBAAgB,CAAC,cAAc,CAAC,CAAA;IAEzC,mDAAmD;IACnD,KAAK,EAAE,mBAAmB,CAAA;CAC3B;AAOD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,oBAAoB,CAChC,MAAM,EAAE,UAAU,EAClB,KAAK,EAAE,qBAAqB,EAC5B,OAAO,CAAC,EAAE,sBAAsB,GAAI,eAAe,CA6BtD;AAGD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,kBAAkB,CAC9B,IAAI,EAAE,iBAAiB,CAAC,cAAc,CAAC,EACvC,OAAO,CAAC,EAAE;IAAE,YAAY,CAAC,EAAE,MAAM,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,CAAA;CAAE,GAC5D,eAAe,CAAC,cAAc,CAAC,CAIlC;AAGD;;;;;;;;;;;GAWG;AACH,wBAAsB,yBAAyB,CAC3C,MAAM,EAAE,kBAAkB,EAC1B,KAAK,EAAE,qBAAqB,EAC5B,OAAO,CAAC,EAAE,sBAAsB,GAAG;IACjC,UAAU,CAAC,EAAE,CAAE,kBAAkB,EAAE,MAAM,KAAM,IAAI,CAAA;CACpD,GAAI,OAAO,CAAC,eAAe,CAAC,CA+BhC;AAGD;;;;;;;;GAQG;AACH,wBAAgB,6BAA6B,CACzC,KAAK,EAAE,qBAAqB,EAC5B,OAAO,CAAC,EAAE,sBAAsB,GAAG;IACjC,UAAU,CAAC,EAAE,CAAE,kBAAkB,EAAE,MAAM,KAAM,IAAI,CAAA;CACpD,GAAI,OAAO,CAAC,eAAe,CAAC,CAGhC"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { StoreByteSource } from "../step/parsing/byte_source.js";
|
|
2
|
+
import { buildColumnarIndexStreaming, buildColumnarIndexStreamingAsync, } from "../step/parsing/streaming_index_builder.js";
|
|
3
|
+
import { ParseResult } from "../step/parsing/step_parser.js";
|
|
4
|
+
import { WindowedStepBufferProvider, } from "../step/step_buffer_provider.js";
|
|
5
|
+
import { EntityTypesIfcCount } from "./ifc4_gen/entity_types_ifc.gen.js";
|
|
6
|
+
import { PrefixTypeIndex } from "../step/parsing/prefix_type_index.js";
|
|
7
|
+
import { StepTypeIndexer } from "../step/indexing/step_type_indexer.js";
|
|
8
|
+
import IfcStepModel from "./ifc_step_model.js";
|
|
9
|
+
import IfcStepParser from "./ifc_step_parser.js";
|
|
10
|
+
// eslint-disable-next-line no-magic-numbers
|
|
11
|
+
const DEFAULT_STREAM_POOL_BYTES = 1024 * 1024;
|
|
12
|
+
/**
|
|
13
|
+
* Open an IFC model from a streamed source with a **fixed-memory parse**
|
|
14
|
+
* (the release-facing Phase B API; composes M0/M1a/M7):
|
|
15
|
+
*
|
|
16
|
+
* - the index builds through a moving window over `source` — the source is
|
|
17
|
+
* never resident in the JS heap, and the index is columnar from birth
|
|
18
|
+
* (no per-record object phase);
|
|
19
|
+
* - the model reads source bytes on demand through a windowed LRU provider
|
|
20
|
+
* over `store` (`ensureResidentByLocalID`/`ByExpressID` page ranges in
|
|
21
|
+
* before synchronous reads — see `DemandResidencyPump` for the demand
|
|
22
|
+
* orchestration).
|
|
23
|
+
*
|
|
24
|
+
* The typical browser wiring: a worker streams the network body **through**
|
|
25
|
+
* to OPFS while feeding the same bytes to `source`; `store` reads back from
|
|
26
|
+
* OPFS. `source.byteLength` and `store.byteLength` must agree.
|
|
27
|
+
*
|
|
28
|
+
* @param source The sequential parse source.
|
|
29
|
+
* @param store The random-access store the model reads from afterwards.
|
|
30
|
+
* @param options See {@link StreamedIfcOpenOptions}.
|
|
31
|
+
* @return {StreamedIfcOpen} The model + header, columns, and diagnostics.
|
|
32
|
+
*/
|
|
33
|
+
export function openStreamedIfcModel(source, store, options) {
|
|
34
|
+
if (store.byteLength !== source.byteLength) {
|
|
35
|
+
throw new Error(`Streaming store byteLength ${store.byteLength} does not match ` +
|
|
36
|
+
`source byteLength ${source.byteLength}`);
|
|
37
|
+
}
|
|
38
|
+
const { header, columns, result, stats } = buildColumnarIndexStreaming(source, IfcStepParser.Instance, options?.pool ?? DEFAULT_STREAM_POOL_BYTES, options?.onRecordIndexed, options?.indexSink);
|
|
39
|
+
if (result !== ParseResult.COMPLETE) {
|
|
40
|
+
return { model: void 0, result, header, columns, stats };
|
|
41
|
+
}
|
|
42
|
+
const provider = new WindowedStepBufferProvider(store, options?.chunkBytes, options?.maxResidentChunks);
|
|
43
|
+
return {
|
|
44
|
+
model: new IfcStepModel(void 0, columns, provider),
|
|
45
|
+
result,
|
|
46
|
+
header,
|
|
47
|
+
columns,
|
|
48
|
+
stats,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* A type index over the IFC records parsed so far, paired with the schema's
|
|
53
|
+
* production indexer — the composed form of {@link PrefixTypeIndex} that a
|
|
54
|
+
* caller needs, since the index is only useful mid-parse if it reads the same
|
|
55
|
+
* live sink the open is filling.
|
|
56
|
+
*
|
|
57
|
+
* ```ts
|
|
58
|
+
* const indexSink = new ColumnarIndexSink<EntityTypesIfc>()
|
|
59
|
+
* const types = ifcPrefixTypeIndex( indexSink )
|
|
60
|
+
* const open = await openStreamedIfcModelAsync( source, store, {
|
|
61
|
+
* indexSink,
|
|
62
|
+
* onProgress: () => reveal( types.expressIDsOfTypes( IfcProduct ) ),
|
|
63
|
+
* } )
|
|
64
|
+
* ```
|
|
65
|
+
*
|
|
66
|
+
* @param sink The sink passed to the open as `indexSink`.
|
|
67
|
+
* @param options Rebuild pacing (see {@link PrefixTypeIndex}).
|
|
68
|
+
* @param options.growthFactor Record growth before a query rebuilds.
|
|
69
|
+
* @param options.minimumRecords Count below which pacing is skipped.
|
|
70
|
+
* @return {PrefixTypeIndex<EntityTypesIfc>} The index over that sink.
|
|
71
|
+
*/
|
|
72
|
+
export function ifcPrefixTypeIndex(sink, options) {
|
|
73
|
+
return new PrefixTypeIndex(sink, new StepTypeIndexer(EntityTypesIfcCount), options);
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Cooperative twin of {@link openStreamedIfcModel}: the index build
|
|
77
|
+
* yields to the event loop and can fill windows from an async
|
|
78
|
+
* {@link ReadableByteSource}. Pass a {@link StoreByteSource} wrapping
|
|
79
|
+
* `store` to parse a file that is already in OPFS without ever
|
|
80
|
+
* holding it as one `ArrayBuffer`.
|
|
81
|
+
*
|
|
82
|
+
* @param source Sync or async parse source.
|
|
83
|
+
* @param store The random-access store the model reads from afterwards.
|
|
84
|
+
* @param options See {@link StreamedIfcOpenOptions} plus `onProgress`.
|
|
85
|
+
* @return {Promise<StreamedIfcOpen>} The model + header, columns, diagnostics.
|
|
86
|
+
*/
|
|
87
|
+
export async function openStreamedIfcModelAsync(source, store, options) {
|
|
88
|
+
if (store.byteLength !== source.byteLength) {
|
|
89
|
+
throw new Error(`Streaming store byteLength ${store.byteLength} does not match ` +
|
|
90
|
+
`source byteLength ${source.byteLength}`);
|
|
91
|
+
}
|
|
92
|
+
const { header, columns, result, stats } = await buildColumnarIndexStreamingAsync(source, IfcStepParser.Instance, options?.pool ?? DEFAULT_STREAM_POOL_BYTES, options?.onRecordIndexed, options?.onProgress, void 0, options?.indexSink);
|
|
93
|
+
if (result !== ParseResult.COMPLETE) {
|
|
94
|
+
return { model: void 0, result, header, columns, stats };
|
|
95
|
+
}
|
|
96
|
+
const provider = new WindowedStepBufferProvider(store, options?.chunkBytes, options?.maxResidentChunks);
|
|
97
|
+
return {
|
|
98
|
+
model: new IfcStepModel(void 0, columns, provider),
|
|
99
|
+
result,
|
|
100
|
+
header,
|
|
101
|
+
columns,
|
|
102
|
+
stats,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Open a windowed IFC model from a store alone — parse windows and
|
|
107
|
+
* post-parse reads share `store`. This is the M1b embedder entry for
|
|
108
|
+
* a file already sitting in OPFS.
|
|
109
|
+
*
|
|
110
|
+
* @param store The source of truth for the file bytes.
|
|
111
|
+
* @param options See {@link openStreamedIfcModelAsync}.
|
|
112
|
+
* @return {Promise<StreamedIfcOpen>} The model + header, columns, diagnostics.
|
|
113
|
+
*/
|
|
114
|
+
export function openStreamedIfcModelFromStore(store, options) {
|
|
115
|
+
return openStreamedIfcModelAsync(new StoreByteSource(store), store, options);
|
|
116
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ifc_stream_open.test.d.ts","sourceRoot":"","sources":["../../../src/ifc/ifc_stream_open.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
/* eslint-disable no-magic-numbers */
|
|
2
|
+
// Phase B3: the release-facing streamed open — fixed-memory columnar parse
|
|
3
|
+
// over a ByteSource, model over a windowed store, columns exposed for the
|
|
4
|
+
// revisit sidecar — behaves like a resident open for reads once ranges are
|
|
5
|
+
// resident.
|
|
6
|
+
import * as fs from "fs";
|
|
7
|
+
import { beforeAll, describe, expect, test } from "@jest/globals";
|
|
8
|
+
import ParsingBuffer from "../parsing/parsing_buffer.js";
|
|
9
|
+
import IfcStepParser from "./ifc_step_parser.js";
|
|
10
|
+
import { ifcPrefixTypeIndex, openStreamedIfcModel, openStreamedIfcModelFromStore, } from "./ifc_stream_open.js";
|
|
11
|
+
import { ColumnarIndexSink } from "../step/parsing/columnar_index.js";
|
|
12
|
+
import { BufferByteSource } from "../step/parsing/byte_source.js";
|
|
13
|
+
import { InMemoryStepByteStore } from "../step/step_buffer_provider.js";
|
|
14
|
+
import { ParseResult } from "../step/parsing/step_parser.js";
|
|
15
|
+
import { hashSource, serializeIndexSidecarFromColumns, deserializeIndexSidecarToColumns, sidecarMatchesSource, } from "../step/parsing/index_sidecar.js";
|
|
16
|
+
import { IfcRoot } from "./ifc4_gen/index.js";
|
|
17
|
+
let bytes;
|
|
18
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
19
|
+
let residentModel;
|
|
20
|
+
beforeAll(() => {
|
|
21
|
+
bytes = new Uint8Array(fs.readFileSync("data/index.ifc"));
|
|
22
|
+
const input = new ParsingBuffer(bytes);
|
|
23
|
+
IfcStepParser.Instance.parseHeader(input);
|
|
24
|
+
residentModel = IfcStepParser.Instance.parseDataToModel(input)[1];
|
|
25
|
+
});
|
|
26
|
+
describe("openStreamedIfcModel (Phase B3)", () => {
|
|
27
|
+
test("a caller-owned sink makes the type index queryable mid-parse", () => {
|
|
28
|
+
// The composed path a consumer of @bldrs-ai/conway/stream actually has:
|
|
29
|
+
// own the sink, hand it to the open, and read the index while the parse
|
|
30
|
+
// is still filling it. Without the sink seam the columns only exist once
|
|
31
|
+
// the open returns, which is too late for anything incremental.
|
|
32
|
+
const indexSink = new ColumnarIndexSink();
|
|
33
|
+
const types = ifcPrefixTypeIndex(indexSink, { growthFactor: 1, minimumRecords: 0 });
|
|
34
|
+
const midParse = [];
|
|
35
|
+
const open = openStreamedIfcModel(new BufferByteSource(bytes), new InMemoryStepByteStore(bytes), {
|
|
36
|
+
pool: 4 * 1024,
|
|
37
|
+
indexSink,
|
|
38
|
+
onRecordIndexed: (localID) => {
|
|
39
|
+
if (localID === 64 || localID === 128) {
|
|
40
|
+
midParse.push([...types.expressIDsOfTypes(IfcRoot)].length);
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
expect(open.result).toBe(ParseResult.COMPLETE);
|
|
45
|
+
expect(midParse).toHaveLength(2);
|
|
46
|
+
expect(midParse[0]).toBeLessThanOrEqual(midParse[1]);
|
|
47
|
+
const finalCount = [...types.expressIDsOfTypes(IfcRoot)].length;
|
|
48
|
+
expect(midParse[1]).toBeLessThanOrEqual(finalCount);
|
|
49
|
+
expect(finalCount).toBe([...residentModel.expressIDsOfTypes(IfcRoot)].length);
|
|
50
|
+
});
|
|
51
|
+
test("opens a model matching the resident parse, with header and columns", () => {
|
|
52
|
+
const open = openStreamedIfcModel(new BufferByteSource(bytes), new InMemoryStepByteStore(bytes), { pool: 4 * 1024 });
|
|
53
|
+
expect(open.result).toBe(ParseResult.COMPLETE);
|
|
54
|
+
expect(open.model).toBeDefined();
|
|
55
|
+
expect(open.header.headers.size).toBeGreaterThan(0);
|
|
56
|
+
expect(open.columns.firstInlineElement).toBeGreaterThan(0);
|
|
57
|
+
const streamedRoots = new Set(open.model.expressIDsOfTypes(IfcRoot));
|
|
58
|
+
const residentRoots = new Set(residentModel.expressIDsOfTypes(IfcRoot));
|
|
59
|
+
expect(streamedRoots).toEqual(residentRoots);
|
|
60
|
+
});
|
|
61
|
+
test("reads work after ensureResident (the windowed contract)", async () => {
|
|
62
|
+
const open = openStreamedIfcModel(new BufferByteSource(bytes), new InMemoryStepByteStore(bytes));
|
|
63
|
+
const model = open.model;
|
|
64
|
+
const expressID = [...model.expressIDsOfTypes(IfcRoot)][0];
|
|
65
|
+
await model.ensureResidentByExpressID(expressID);
|
|
66
|
+
const element = model.getElementByExpressID(expressID);
|
|
67
|
+
expect(element?.expressID).toBe(expressID);
|
|
68
|
+
});
|
|
69
|
+
test("record events fire live during the open", () => {
|
|
70
|
+
let events = 0;
|
|
71
|
+
openStreamedIfcModel(new BufferByteSource(bytes), new InMemoryStepByteStore(bytes), { onRecordIndexed: () => void ++events });
|
|
72
|
+
expect(events).toBe(openStreamedIfcModel(new BufferByteSource(bytes), new InMemoryStepByteStore(bytes)).columns.firstInlineElement);
|
|
73
|
+
});
|
|
74
|
+
test("the returned columns serialize to a trustable revisit sidecar", () => {
|
|
75
|
+
const open = openStreamedIfcModel(new BufferByteSource(bytes), new InMemoryStepByteStore(bytes));
|
|
76
|
+
const hash = hashSource(bytes);
|
|
77
|
+
const sidecar = serializeIndexSidecarFromColumns(open.columns, bytes.byteLength, hash);
|
|
78
|
+
const restored = deserializeIndexSidecarToColumns(sidecar);
|
|
79
|
+
expect(sidecarMatchesSource(
|
|
80
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
81
|
+
restored, bytes.byteLength, hash)).toBe(true);
|
|
82
|
+
expect(restored.columns.count).toBe(open.columns.firstInlineElement);
|
|
83
|
+
});
|
|
84
|
+
test("rejects a store whose length disagrees with the source", () => {
|
|
85
|
+
expect(() => openStreamedIfcModel(new BufferByteSource(bytes), new InMemoryStepByteStore(bytes.subarray(0, 100))))
|
|
86
|
+
.toThrow(/does not match/);
|
|
87
|
+
});
|
|
88
|
+
test("releaseSourceViews still lets a later read re-acquire", async () => {
|
|
89
|
+
const store = new InMemoryStepByteStore(bytes);
|
|
90
|
+
const fromStore = await openStreamedIfcModelFromStore(store, { pool: 4 * 1024 });
|
|
91
|
+
const model = fromStore.model;
|
|
92
|
+
const expressID = [...model.expressIDsOfTypes(IfcRoot)][0];
|
|
93
|
+
const localID = model.resolveExpressID(expressID);
|
|
94
|
+
await model.ensureResidentByLocalID(localID);
|
|
95
|
+
const first = model.getElementByExpressID(expressID);
|
|
96
|
+
expect(first).toBeDefined();
|
|
97
|
+
const args = first.extractLineArguments();
|
|
98
|
+
expect(args.length).toBeGreaterThan(0);
|
|
99
|
+
model.releaseSourceViews([localID]);
|
|
100
|
+
await model.ensureResidentByLocalID(localID);
|
|
101
|
+
const again = model.getElementByExpressID(expressID);
|
|
102
|
+
expect(again.extractLineArguments().length).toBe(args.length);
|
|
103
|
+
});
|
|
104
|
+
test("extractParseBuffer rematerialises after releaseSourceViews", async () => {
|
|
105
|
+
const store = new InMemoryStepByteStore(bytes);
|
|
106
|
+
const fromStore = await openStreamedIfcModelFromStore(store, { pool: 4 * 1024 });
|
|
107
|
+
const model = fromStore.model;
|
|
108
|
+
const expressID = [...model.expressIDsOfTypes(IfcRoot)][0];
|
|
109
|
+
const localID = model.resolveExpressID(expressID);
|
|
110
|
+
await model.ensureResidentByLocalID(localID);
|
|
111
|
+
const first = model.getElementByExpressID(expressID);
|
|
112
|
+
// Populate vtable + buffer the way a first extract does.
|
|
113
|
+
expect(first.extractLineArguments().length).toBeGreaterThan(0);
|
|
114
|
+
// A real heap rather than a stubbed HEAPU8.set. extractParseBuffer builds
|
|
115
|
+
// its destination through wasmHeapView now, which reconciles the pointer
|
|
116
|
+
// against the heap's actual buffer before writing (#485) - so the fake has
|
|
117
|
+
// to have one. It also makes the assertions stronger: the bytes are
|
|
118
|
+
// checked where they landed, not just counted on the way past.
|
|
119
|
+
const wasmHeap = new Uint8Array(4 * 1024);
|
|
120
|
+
const fakeModule = { HEAPU8: wasmHeap };
|
|
121
|
+
const copied = [];
|
|
122
|
+
const fakeResult = { resize: (n) => {
|
|
123
|
+
copied.push(n);
|
|
124
|
+
return 0;
|
|
125
|
+
} };
|
|
126
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
127
|
+
first.extractParseBuffer(0, 0, 0, fakeResult, fakeModule, true);
|
|
128
|
+
expect(copied[0]).toBeGreaterThan(0);
|
|
129
|
+
// Landed in the heap, at the pointer resize handed back.
|
|
130
|
+
const written = Array.from(wasmHeap.subarray(0, copied[0]));
|
|
131
|
+
expect(written.some((byte) => byte !== 0)).toBe(true);
|
|
132
|
+
model.releaseSourceViews([localID]);
|
|
133
|
+
await model.ensureResidentByLocalID(localID);
|
|
134
|
+
// Same entity object — vtable kept, buffer dropped. This is the
|
|
135
|
+
// PSB "Cannot read properties of undefined (reading 'subarray')" path.
|
|
136
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
137
|
+
first.extractParseBuffer(0, 0, 0, fakeResult, fakeModule, true);
|
|
138
|
+
expect(copied[1]).toBe(copied[0]);
|
|
139
|
+
});
|
|
140
|
+
test("ensureResidentClosureByLocalID visits every #ref hop", async () => {
|
|
141
|
+
const store = new InMemoryStepByteStore(bytes);
|
|
142
|
+
const fromStore = await openStreamedIfcModelFromStore(store, { pool: 4 * 1024 });
|
|
143
|
+
const model = fromStore.model;
|
|
144
|
+
const expressID = [...model.expressIDsOfTypes(IfcRoot)][0];
|
|
145
|
+
const localID = model.resolveExpressID(expressID);
|
|
146
|
+
await model.ensureResidentByLocalID(localID);
|
|
147
|
+
const refs = model.referencedExpressIDs(localID);
|
|
148
|
+
expect(refs.length).toBeGreaterThan(0);
|
|
149
|
+
const visited = await model.ensureResidentClosureByLocalID(localID);
|
|
150
|
+
expect(visited.has(localID)).toBe(true);
|
|
151
|
+
expect(model.typeIDOf(localID)).toBeDefined();
|
|
152
|
+
for (const refExpressID of refs) {
|
|
153
|
+
const refLocalID = model.resolveExpressID(refExpressID);
|
|
154
|
+
if (refLocalID !== void 0) {
|
|
155
|
+
expect(visited.has(refLocalID)).toBe(true);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
test("ensureResidentClosureByLocalID skip-descend does not follow #refs", async () => {
|
|
160
|
+
const store = new InMemoryStepByteStore(bytes);
|
|
161
|
+
const fromStore = await openStreamedIfcModelFromStore(store, { pool: 4 * 1024 });
|
|
162
|
+
const model = fromStore.model;
|
|
163
|
+
const expressID = [...model.expressIDsOfTypes(IfcRoot)][0];
|
|
164
|
+
const localID = model.resolveExpressID(expressID);
|
|
165
|
+
await model.ensureResidentByLocalID(localID);
|
|
166
|
+
const refs = model.referencedExpressIDs(localID);
|
|
167
|
+
expect(refs.length).toBeGreaterThan(0);
|
|
168
|
+
const leafSpans = [];
|
|
169
|
+
const visited = await model.ensureResidentClosureByLocalID(localID, void 0, new Set(), (id) => id === localID, leafSpans);
|
|
170
|
+
expect(visited.has(localID)).toBe(true);
|
|
171
|
+
expect(visited.size).toBe(1);
|
|
172
|
+
expect(leafSpans.length).toBeGreaterThan(0);
|
|
173
|
+
expect(leafSpans[0].length).toBeGreaterThan(0);
|
|
174
|
+
});
|
|
175
|
+
test("spanLeaf spans children; !descend without spanLeaf still visits them", async () => {
|
|
176
|
+
const store = new InMemoryStepByteStore(bytes);
|
|
177
|
+
const fromStore = await openStreamedIfcModelFromStore(store, { pool: 4 * 1024 });
|
|
178
|
+
const model = fromStore.model;
|
|
179
|
+
const expressID = [...model.expressIDsOfTypes(IfcRoot)][0];
|
|
180
|
+
const localID = model.resolveExpressID(expressID);
|
|
181
|
+
await model.ensureResidentByLocalID(localID);
|
|
182
|
+
const refs = model.referencedExpressIDs(localID);
|
|
183
|
+
expect(refs.length).toBeGreaterThan(0);
|
|
184
|
+
const leafSpans = [];
|
|
185
|
+
const visited = await model.ensureResidentClosureByLocalID(localID, void 0, new Set(), (id) => id === localID, leafSpans, () => false);
|
|
186
|
+
expect(visited.has(localID)).toBe(true);
|
|
187
|
+
expect(visited.size).toBeGreaterThan(1);
|
|
188
|
+
expect(leafSpans.length).toBe(0);
|
|
189
|
+
});
|
|
190
|
+
test("spansOfExpressIDs covers each record without the holes between", async () => {
|
|
191
|
+
const store = new InMemoryStepByteStore(bytes);
|
|
192
|
+
const fromStore = await openStreamedIfcModelFromStore(store, { pool: 4 * 1024 });
|
|
193
|
+
const model = fromStore.model;
|
|
194
|
+
const expressIDs = [...model.expressIDsOfTypes(IfcRoot)];
|
|
195
|
+
expect(expressIDs.length).toBeGreaterThan(1);
|
|
196
|
+
const spans = model.spansOfExpressIDs(expressIDs);
|
|
197
|
+
expect(spans.length).toBeGreaterThan(0);
|
|
198
|
+
let covered = 0;
|
|
199
|
+
let hullMin = Infinity;
|
|
200
|
+
let hullMax = 0;
|
|
201
|
+
for (const span of spans) {
|
|
202
|
+
covered += span.length;
|
|
203
|
+
}
|
|
204
|
+
for (const refExpressID of expressIDs) {
|
|
205
|
+
const localID = model.resolveExpressID(refExpressID);
|
|
206
|
+
const address = model.recordAddress(localID);
|
|
207
|
+
const end = address + model.recordLength(localID);
|
|
208
|
+
if (address < hullMin) {
|
|
209
|
+
hullMin = address;
|
|
210
|
+
}
|
|
211
|
+
if (end > hullMax) {
|
|
212
|
+
hullMax = end;
|
|
213
|
+
}
|
|
214
|
+
const inside = spans.some((span) => address >= span.address && end <= span.address + span.length);
|
|
215
|
+
expect(inside).toBe(true);
|
|
216
|
+
}
|
|
217
|
+
// IfcRoot rows are scattered through index.ifc — the coalesced
|
|
218
|
+
// spans must not be the single hull from first to last.
|
|
219
|
+
expect(covered).toBeLessThan(hullMax - hullMin);
|
|
220
|
+
});
|
|
221
|
+
test("openStreamedIfcModelFromStore matches the sync open", async () => {
|
|
222
|
+
const store = new InMemoryStepByteStore(bytes);
|
|
223
|
+
const fromStore = await openStreamedIfcModelFromStore(store, { pool: 4 * 1024 });
|
|
224
|
+
const streamed = openStreamedIfcModel(new BufferByteSource(bytes), store, { pool: 4 * 1024 });
|
|
225
|
+
expect(fromStore.result).toBe(ParseResult.COMPLETE);
|
|
226
|
+
expect(fromStore.model.isSourceExternal).toBe(true);
|
|
227
|
+
expect([...fromStore.model.expressIDsOfTypes(IfcRoot)])
|
|
228
|
+
.toEqual([...streamed.model.expressIDsOfTypes(IfcRoot)]);
|
|
229
|
+
});
|
|
230
|
+
});
|