okf-parser 0.43.0__tar.gz → 0.45.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {okf_parser-0.43.0 → okf_parser-0.45.2}/Cargo.lock +8 -8
- {okf_parser-0.43.0 → okf_parser-0.45.2}/PKG-INFO +2 -2
- {okf_parser-0.43.0 → okf_parser-0.45.2}/README.md +1 -1
- {okf_parser-0.43.0 → okf_parser-0.45.2}/benchmarks/README.md +2 -2
- {okf_parser-0.43.0 → okf_parser-0.45.2}/benchmarks/python_parser.py +11 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/benchmarks/rust_facts.py +11 -0
- okf_parser-0.45.2/changelog/0.43.1.md +10 -0
- okf_parser-0.45.2/changelog/0.44.0.md +10 -0
- okf_parser-0.45.2/changelog/0.45.0.md +10 -0
- okf_parser-0.45.2/changelog/0.45.1.md +9 -0
- okf_parser-0.45.2/changelog/0.45.2.md +11 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/native-npm-linux-x64/package.json +1 -1
- {okf_parser-0.43.0 → okf_parser-0.45.2}/okf-engine/Cargo.toml +1 -1
- {okf_parser-0.43.0 → okf_parser-0.45.2}/pyproject.toml +1 -1
- {okf_parser-0.43.0 → okf_parser-0.45.2}/rust-core/Cargo.toml +2 -2
- {okf_parser-0.43.0 → okf_parser-0.45.2}/scripts/check_no_duckdb_link.py +7 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/scripts/native_from_wheel.py +7 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/scripts/registry_state.py +24 -4
- {okf_parser-0.43.0 → okf_parser-0.45.2}/scripts/release_contract.py +32 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/src/okf_parser/__init__.py +3 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/src/okf_parser/bundle_import.py +105 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/src/okf_parser/cli.py +4 -0
- okf_parser-0.45.2/src/okf_parser/concepts.py +118 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/src/okf_parser/models.py +5 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/src/okf_parser/service.py +2 -0
- okf_parser-0.45.2/tests/test_concepts.py +177 -0
- okf_parser-0.45.2/tests/test_import_preview_token.py +143 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/tests/test_mcp.py +35 -8
- okf_parser-0.45.2/tests/test_registry_state_native_npm.py +172 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/tests/test_release_contract.py +65 -4
- {okf_parser-0.43.0 → okf_parser-0.45.2}/typescript/package-lock.json +4 -4
- {okf_parser-0.43.0 → okf_parser-0.45.2}/typescript/package.json +2 -2
- okf_parser-0.45.2/typescript/src/version.ts +1 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/typescript-duckdb/package-lock.json +5 -5
- {okf_parser-0.43.0 → okf_parser-0.45.2}/typescript-duckdb/package.json +2 -2
- {okf_parser-0.43.0 → okf_parser-0.45.2}/uv.lock +3 -3
- okf_parser-0.43.0/typescript/src/version.ts +0 -1
- {okf_parser-0.43.0 → okf_parser-0.45.2}/.gitignore +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/Cargo.toml +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/action.yml +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/benchmarks/__init__.py +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/benchmarks/results/2026-08-08-rust-mvp.md +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/benchmarks/results/2026-08-09-rust-e2e.md +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/benchmarks/rust-facts.mjs +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/benchmarks/typescript-parser.mjs +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/changelog/0.1.0.md +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/changelog/0.10.0.md +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/changelog/0.11.0.md +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/changelog/0.11.1.md +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/changelog/0.12.0.md +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/changelog/0.13.0.md +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/changelog/0.14.0.md +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/changelog/0.14.1.md +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/changelog/0.14.2.md +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/changelog/0.15.0.md +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/changelog/0.15.1.md +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/changelog/0.15.2.md +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/changelog/0.15.3.md +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/changelog/0.15.4.md +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/changelog/0.16.0.md +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/changelog/0.17.0.md +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/changelog/0.18.0.md +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/changelog/0.18.1.md +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/changelog/0.19.0.md +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/changelog/0.19.1.md +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/changelog/0.2.0.md +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/changelog/0.20.0.md +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/changelog/0.20.1.md +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/changelog/0.21.0.md +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/changelog/0.22.0.md +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/changelog/0.23.0.md +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/changelog/0.24.0.md +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/changelog/0.25.0.md +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/changelog/0.26.0.md +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/changelog/0.27.0.md +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/changelog/0.28.0.md +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/changelog/0.29.0.md +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/changelog/0.3.0.md +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/changelog/0.30.0.md +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/changelog/0.31.0.md +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/changelog/0.32.0.md +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/changelog/0.32.1.md +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/changelog/0.33.0.md +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/changelog/0.34.0.md +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/changelog/0.35.0.md +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/changelog/0.36.0.md +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/changelog/0.37.0.md +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/changelog/0.38.0.md +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/changelog/0.39.0.md +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/changelog/0.39.1.md +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/changelog/0.4.0.md +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/changelog/0.40.0.md +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/changelog/0.41.0.md +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/changelog/0.41.1.md +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/changelog/0.41.2.md +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/changelog/0.41.3.md +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/changelog/0.42.0.md +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/changelog/0.42.1.md +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/changelog/0.42.2.md +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/changelog/0.42.3.md +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/changelog/0.42.4.md +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/changelog/0.42.5.md +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/changelog/0.42.6.md +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/changelog/0.42.7.md +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/changelog/0.42.8.md +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/changelog/0.43.0.md +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/changelog/0.5.0.md +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/changelog/0.5.1.md +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/changelog/0.6.0.md +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/changelog/0.7.0.md +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/changelog/0.8.0.md +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/changelog/0.9.0.md +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/conformance/content-digests.json +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/conformance/exclusion.json +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/conformance/formatting.json +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/conformance/frontmatter.json +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/conformance/schema-inference.json +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/conformance/schema-output.json +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/conformance/type-spec-slugs.json +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/docs/architecture.md +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/docs/cli.md +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/docs/graphql.md +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/docs/native-engine.md +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/docs/releasing.md +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/fastmcp.json +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/native-npm-linux-x64/README.md +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/okf-engine/src/engine.rs +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/okf-engine/src/lib.rs +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/okf-engine/src/yaml.rs +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/rfcs/0001-python-model-codegen.md +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/rfcs/0002-typescript-sibling-package.md +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/rfcs/0003-resumable-multi-registry-release.md +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/rfcs/0004-gitignore-exclusion-semantics.md +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/rfcs/0005-relational-frontmatter-writes.md +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/rfcs/0006-declared-column-types.md +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/rfcs/0007-declared-relational-constraints.md +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/rfcs/0008-effect-aware-mcp-writes.md +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/rust-core/.gitignore +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/rust-core/src/engine.rs +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/rust-core/src/main.rs +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/scripts/__init__.py +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/src/okf_parser/apply.py +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/src/okf_parser/bundle.py +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/src/okf_parser/classification.py +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/src/okf_parser/declared_schema.py +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/src/okf_parser/digests.py +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/src/okf_parser/discovery.py +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/src/okf_parser/duckdb.py +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/src/okf_parser/duckdb_types.py +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/src/okf_parser/edit.py +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/src/okf_parser/engine.py +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/src/okf_parser/exclusion.py +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/src/okf_parser/formatting.py +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/src/okf_parser/graphql_adapter.py +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/src/okf_parser/ingestion.py +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/src/okf_parser/markdown_style.py +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/src/okf_parser/parser.py +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/src/okf_parser/pydantic_projection.py +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/src/okf_parser/relational_schema.py +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/src/okf_parser/rust_core.py +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/src/okf_parser/schema_contract.py +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/src/okf_parser/schema_export.py +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/src/okf_parser/schema_lexemes.py +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/src/okf_parser/spec_scaffold.py +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/src/okf_parser/type_specs.py +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/src/okf_parser/typed_relations.py +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/src/okf_parser/typed_tables.py +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/src/okf_parser/write_support.py +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/tests/__init__.py +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/tests/fixtures/__init__.py +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/tests/fixtures/declared_parity.expected.json +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/tests/fixtures/declared_parity.schema.sql +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/tests/fixtures/pydantic_adversarial_generated.py +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/tests/fixtures/pydantic_generated.py +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/tests/test_apply.py +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/tests/test_apply_preview_token.py +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/tests/test_apply_preview_token_noop.py +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/tests/test_bundle.py +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/tests/test_bundle_import.py +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/tests/test_bundle_import_reserved.py +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/tests/test_classification.py +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/tests/test_cli.py +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/tests/test_conformance.py +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/tests/test_content_digests.py +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/tests/test_declared_schema.py +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/tests/test_declared_schema_correctness.py +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/tests/test_duckdb.py +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/tests/test_duckdb_types.py +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/tests/test_edit.py +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/tests/test_engine_resolution.py +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/tests/test_exclusion.py +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/tests/test_exclusion_integration.py +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/tests/test_formatting.py +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/tests/test_formatting_conformance.py +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/tests/test_graphql_adapter.py +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/tests/test_ingestion.py +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/tests/test_markdown_style.py +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/tests/test_native_from_wheel.py +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/tests/test_parser.py +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/tests/test_pydantic_source.py +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/tests/test_registry_state.py +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/tests/test_relational_schema.py +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/tests/test_rust_core.py +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/tests/test_schema_export.py +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/tests/test_schema_inference_conformance.py +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/tests/test_schema_output_conformance.py +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/tests/test_spec_scaffold.py +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/tests/test_type_specs.py +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/tests/test_typed_relations.py +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/typescript/.npmignore +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/typescript/LICENSE +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/typescript/README.md +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/typescript/scripts/smoke-mcp-stdio.mjs +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/typescript/src/cli.ts +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/typescript/src/core.ts +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/typescript/src/digests.ts +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/typescript/src/engine.ts +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/typescript/src/formatting.ts +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/typescript/src/index.ts +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/typescript/src/ingestion.ts +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/typescript/src/lexemes.ts +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/typescript/src/mcp-cli.ts +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/typescript/src/mcp.ts +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/typescript/src/rust-core.ts +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/typescript/src/schema.ts +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/typescript/src/type-specs.ts +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/typescript/test/bundle.test.ts +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/typescript/test/classification.test.ts +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/typescript/test/content-digests.test.ts +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/typescript/test/engine-resolution.test.ts +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/typescript/test/exclusion.test.ts +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/typescript/test/formatting.test.ts +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/typescript/test/frontmatter.test.ts +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/typescript/test/ingestion.test.ts +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/typescript/test/mcp.test.ts +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/typescript/test/schema-inference-conformance.test.ts +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/typescript/test/schema-output-conformance.test.ts +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/typescript/test/schema.test.ts +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/typescript/test/type-specs.test.ts +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/typescript/tsconfig.build.json +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/typescript/tsconfig.json +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/typescript-duckdb/LICENSE +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/typescript-duckdb/README.md +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/typescript-duckdb/src/cli.ts +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/typescript-duckdb/src/declared.ts +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/typescript-duckdb/src/index.ts +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/typescript-duckdb/src/normalized.ts +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/typescript-duckdb/src/session.ts +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/typescript-duckdb/src/typed.ts +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/typescript-duckdb/test/current-load-options.test.ts +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/typescript-duckdb/test/digests.test.ts +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/typescript-duckdb/test/duckdb.test.ts +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/typescript-duckdb/test/typed.test.ts +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/typescript-duckdb/tsconfig.build.json +0 -0
- {okf_parser-0.43.0 → okf_parser-0.45.2}/typescript-duckdb/tsconfig.json +0 -0
|
@@ -115,9 +115,9 @@ checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649"
|
|
|
115
115
|
|
|
116
116
|
[[package]]
|
|
117
117
|
name = "cc"
|
|
118
|
-
version = "1.4.
|
|
118
|
+
version = "1.4.3"
|
|
119
119
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
120
|
-
checksum = "
|
|
120
|
+
checksum = "509591b7bcd67f4ef775afad7662703b4935daaa6ec0e5605cfb1090b32a2b6d"
|
|
121
121
|
dependencies = [
|
|
122
122
|
"find-msvc-tools",
|
|
123
123
|
"shlex",
|
|
@@ -250,9 +250,9 @@ dependencies = [
|
|
|
250
250
|
|
|
251
251
|
[[package]]
|
|
252
252
|
name = "either"
|
|
253
|
-
version = "1.
|
|
253
|
+
version = "1.18.0"
|
|
254
254
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
255
|
-
checksum = "
|
|
255
|
+
checksum = "252afb9ae5eaa683babdc6a068b3f5726eb19e05070c731f9b2a23a7c3e8ed34"
|
|
256
256
|
|
|
257
257
|
[[package]]
|
|
258
258
|
name = "encoding_rs"
|
|
@@ -265,9 +265,9 @@ dependencies = [
|
|
|
265
265
|
|
|
266
266
|
[[package]]
|
|
267
267
|
name = "find-msvc-tools"
|
|
268
|
-
version = "0.1.
|
|
268
|
+
version = "0.1.11"
|
|
269
269
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
270
|
-
checksum = "
|
|
270
|
+
checksum = "d45db016d36b838f563236e9193d0ee6ce38f3f68b6c94e914b4929c96bbb890"
|
|
271
271
|
|
|
272
272
|
[[package]]
|
|
273
273
|
name = "foldhash"
|
|
@@ -447,7 +447,7 @@ dependencies = [
|
|
|
447
447
|
|
|
448
448
|
[[package]]
|
|
449
449
|
name = "okf-core"
|
|
450
|
-
version = "0.
|
|
450
|
+
version = "0.45.2"
|
|
451
451
|
dependencies = [
|
|
452
452
|
"clap",
|
|
453
453
|
"okf-engine",
|
|
@@ -457,7 +457,7 @@ dependencies = [
|
|
|
457
457
|
|
|
458
458
|
[[package]]
|
|
459
459
|
name = "okf-engine"
|
|
460
|
-
version = "0.
|
|
460
|
+
version = "0.45.2"
|
|
461
461
|
dependencies = [
|
|
462
462
|
"chrono",
|
|
463
463
|
"ignore",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: okf-parser
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.45.2
|
|
4
4
|
Requires-Dist: cyclopts>=4.22,<5
|
|
5
5
|
Requires-Dist: duckdb>=1.4,<2
|
|
6
6
|
Requires-Dist: fastmcp>=3.4,<4
|
|
@@ -231,7 +231,7 @@ Add the repository as a CI check:
|
|
|
231
231
|
```yaml
|
|
232
232
|
steps:
|
|
233
233
|
- uses: actions/checkout@v7
|
|
234
|
-
- uses: franklinbaldo/okf-parser@v0.
|
|
234
|
+
- uses: franklinbaldo/okf-parser@v0.45.2
|
|
235
235
|
with:
|
|
236
236
|
path: knowledge
|
|
237
237
|
```
|
|
@@ -14,7 +14,7 @@ The default matrix uses 100, 1,000 and 5,000 concepts, five measured rounds and
|
|
|
14
14
|
one unmeasured warm-up round:
|
|
15
15
|
|
|
16
16
|
```bash
|
|
17
|
-
uv run
|
|
17
|
+
uv run benchmarks/python_parser.py
|
|
18
18
|
npm --prefix typescript run build
|
|
19
19
|
node benchmarks/typescript-parser.mjs
|
|
20
20
|
```
|
|
@@ -22,7 +22,7 @@ node benchmarks/typescript-parser.mjs
|
|
|
22
22
|
Use the same parameters in both runtimes when changing the corpus:
|
|
23
23
|
|
|
24
24
|
```bash
|
|
25
|
-
uv run
|
|
25
|
+
uv run benchmarks/python_parser.py \
|
|
26
26
|
--sizes 100,1000 --body-paragraphs 8 --rounds 7 \
|
|
27
27
|
--read-concurrencies 1,4,8,16,32,64
|
|
28
28
|
|
|
@@ -1,3 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env -S uv run --script
|
|
2
|
+
#
|
|
3
|
+
# /// script
|
|
4
|
+
# requires-python = ">=3.12"
|
|
5
|
+
# dependencies = [
|
|
6
|
+
# "okf-parser",
|
|
7
|
+
# ]
|
|
8
|
+
#
|
|
9
|
+
# [tool.uv.sources]
|
|
10
|
+
# okf-parser = { path = "..", editable = true }
|
|
11
|
+
# ///
|
|
1
12
|
"""Reproducible parsing benchmarks for the Python runtime."""
|
|
2
13
|
|
|
3
14
|
from __future__ import annotations
|
|
@@ -1,3 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env -S uv run --script
|
|
2
|
+
#
|
|
3
|
+
# /// script
|
|
4
|
+
# requires-python = ">=3.12"
|
|
5
|
+
# dependencies = [
|
|
6
|
+
# "okf-parser",
|
|
7
|
+
# ]
|
|
8
|
+
#
|
|
9
|
+
# [tool.uv.sources]
|
|
10
|
+
# okf-parser = { path = "..", editable = true }
|
|
11
|
+
# ///
|
|
1
12
|
"""Compare native Python Markdown facts with the experimental Rust batch core."""
|
|
2
13
|
|
|
3
14
|
from __future__ import annotations
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: Release
|
|
3
|
+
title: okf-parser 0.43.1
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# okf-parser 0.43.1
|
|
7
|
+
|
|
8
|
+
- Fix public-registry preflight so the native npm companion is treated as an npm artifact rather than a PyPI file.
|
|
9
|
+
- Verify `okf-parser-native-linux-x64` version and integrity alongside the main TypeScript package and DuckDB adapter.
|
|
10
|
+
- Include the native npm package in resumable publication planning and in the release completeness decision.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: Release
|
|
3
|
+
title: okf-parser 0.44.0
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# okf-parser 0.44.0
|
|
7
|
+
|
|
8
|
+
- Expose parser-owned concept records for application-facing lookup by concept id or in-bundle Markdown path.
|
|
9
|
+
- Expose validated structured frontmatter from `ConceptRecord`.
|
|
10
|
+
- Add generic frontmatter resource-relation resolution with optional target-type filtering, so consumers do not reparse Markdown or implement bundle containment themselves.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: Release
|
|
3
|
+
title: okf-parser 0.45.0
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# okf-parser 0.45.0
|
|
7
|
+
|
|
8
|
+
- Add an opaque import `preview_token` that binds a reviewed tabular source, import options, candidate paths and current destination states.
|
|
9
|
+
- Let import writes accept `expected_preview_token` and fail before any filesystem mutation when the source or a relevant destination changed after preview.
|
|
10
|
+
- Carry the parser-owned token through the Python service, CLI and MCP write surface so consumers such as Astronauta can remain preview-first without reimplementing import state.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: Release
|
|
3
|
+
title: okf-parser 0.45.1
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# okf-parser 0.45.1
|
|
7
|
+
|
|
8
|
+
- Make `verify-source` enforce Rust crate identity together with the workspace version: `rust-core`, `okf-engine`, and the internal `rust-core -> okf-engine` dependency must all match.
|
|
9
|
+
- Add causal regression tests for drifted crate and dependency versions, closing the gap where the documented release contract allowed Rust metadata to lag behind for multiple releases.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: Release
|
|
3
|
+
title: okf-parser 0.45.2
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# okf-parser 0.45.2
|
|
7
|
+
|
|
8
|
+
- Restore the executable bits on the six repository scripts that carry a `#!/usr/bin/env -S uv run --script` shebang (`scripts/release_contract.py`, `scripts/registry_state.py`, `scripts/native_from_wheel.py`, `scripts/check_no_duckdb_link.py`, `benchmarks/python_parser.py`, `benchmarks/rust_facts.py`). The PEP 723 migration in 0.45.1's follow-up commit committed them as `100644`, so Ruff reported `EXE001` and the whole quality gate stopped before running. Consumers of the source distribution now get scripts that are runnable as declared.
|
|
9
|
+
- Ship the PEP 723 inline dependency headers themselves in the source distribution: the release and benchmark scripts declare `requires-python` and their dependencies inline, so they run standalone with `uv run` without the repository's own environment.
|
|
10
|
+
|
|
11
|
+
No change to the public Python API, the CLI surface, or the wire protocol; the wheel payload is identical to 0.45.1.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name = "okf-core"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.45.2"
|
|
4
4
|
edition = "2024"
|
|
5
5
|
license = "MIT"
|
|
6
6
|
description = "Native OKF CLI and compatibility adapters"
|
|
@@ -11,6 +11,6 @@ path = "src/main.rs"
|
|
|
11
11
|
|
|
12
12
|
[dependencies]
|
|
13
13
|
clap = { version = "4", features = ["derive"] }
|
|
14
|
-
okf-engine = { path = "../okf-engine", version = "0.
|
|
14
|
+
okf-engine = { path = "../okf-engine", version = "0.45.2" }
|
|
15
15
|
serde = { version = "1", features = ["derive"] }
|
|
16
16
|
serde_json = "1"
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
#!/usr/bin/env -S uv run --script
|
|
2
|
+
#
|
|
3
|
+
# /// script
|
|
4
|
+
# requires-python = ">=3.12"
|
|
5
|
+
# dependencies = [
|
|
6
|
+
# ]
|
|
7
|
+
# ///
|
|
1
8
|
"""Prove a built wheel's executable does not link against a DuckDB library.
|
|
2
9
|
|
|
3
10
|
The Python distribution depends on `duckdb` and runs its DuckDB export
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
#!/usr/bin/env -S uv run --script
|
|
2
|
+
#
|
|
3
|
+
# /// script
|
|
4
|
+
# requires-python = ">=3.12"
|
|
5
|
+
# dependencies = [
|
|
6
|
+
# ]
|
|
7
|
+
# ///
|
|
1
8
|
"""Inspect public package registries without performing any mutation."""
|
|
2
9
|
|
|
3
10
|
from __future__ import annotations
|
|
@@ -114,9 +121,13 @@ def _string(mapping: dict[str, object], key: str, label: str) -> str:
|
|
|
114
121
|
# Not a fixed enum here on purpose: registry_state.py stays decoupled from
|
|
115
122
|
# release_contract.py's exact wheel-kind names (one per supported platform,
|
|
116
123
|
# e.g. "python-wheel-linux-x86_64") so adding/removing a target platform
|
|
117
|
-
# never requires touching this file. Any kind other than
|
|
118
|
-
#
|
|
119
|
-
_NPM_KINDS: Final = {"npm-parser", "npm-duckdb"}
|
|
124
|
+
# never requires touching this file. Any kind other than npm package kinds is
|
|
125
|
+
# treated as a PyPI artifact for okf-parser.
|
|
126
|
+
_NPM_KINDS: Final = {"npm-parser", "npm-duckdb", "npm-native"}
|
|
127
|
+
# Keep compatibility with older manifests/tests that predate the native npm
|
|
128
|
+
# artifact; release_contract.py is the authority that requires the complete
|
|
129
|
+
# current release set. When npm-native is present here, it must be inspected.
|
|
130
|
+
_REQUIRED_NPM_KINDS: Final = {"npm-parser", "npm-duckdb"}
|
|
120
131
|
_MIN_PYPI_ARTIFACTS: Final = 2 # at least one wheel + the sdist
|
|
121
132
|
|
|
122
133
|
|
|
@@ -134,7 +145,7 @@ def _artifact_index(manifest: dict[str, object]) -> tuple[str, dict[str, Artifac
|
|
|
134
145
|
if kind in indexed:
|
|
135
146
|
_fail(f"duplicate manifest artifact kind {kind!r}")
|
|
136
147
|
indexed[kind] = item
|
|
137
|
-
if not
|
|
148
|
+
if not _REQUIRED_NPM_KINDS.issubset(indexed):
|
|
138
149
|
_fail("manifest artifact set is missing an npm package")
|
|
139
150
|
pypi_kinds = set(indexed) - _NPM_KINDS
|
|
140
151
|
if len(pypi_kinds) < _MIN_PYPI_ARTIFACTS:
|
|
@@ -351,6 +362,15 @@ def inspect_registry_state(
|
|
|
351
362
|
_npm_state("okf-parser", artifacts["npm-parser"], fetch, timeout),
|
|
352
363
|
_npm_state("okf-parser-duckdb", artifacts["npm-duckdb"], fetch, timeout),
|
|
353
364
|
]
|
|
365
|
+
if "npm-native" in artifacts:
|
|
366
|
+
entries.append(
|
|
367
|
+
_npm_state(
|
|
368
|
+
"okf-parser-native-linux-x64",
|
|
369
|
+
artifacts["npm-native"],
|
|
370
|
+
fetch,
|
|
371
|
+
timeout,
|
|
372
|
+
)
|
|
373
|
+
)
|
|
354
374
|
states = [entry["state"] for entry in entries]
|
|
355
375
|
allowed = {"absent", "present_expected"}
|
|
356
376
|
return {
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
#!/usr/bin/env -S uv run --script
|
|
2
|
+
#
|
|
3
|
+
# /// script
|
|
4
|
+
# requires-python = ">=3.12"
|
|
5
|
+
# dependencies = [
|
|
6
|
+
# ]
|
|
7
|
+
# ///
|
|
1
8
|
"""Validate synchronized release metadata and artifact bytes."""
|
|
2
9
|
|
|
3
10
|
from __future__ import annotations
|
|
@@ -320,11 +327,36 @@ def _verify_changelog(root: Path, version: str) -> Path:
|
|
|
320
327
|
return path
|
|
321
328
|
|
|
322
329
|
|
|
330
|
+
def _verify_rust_crates(root: Path, version: str) -> None:
|
|
331
|
+
"""Check the Rust crate versions against the workspace version.
|
|
332
|
+
|
|
333
|
+
docs/releasing.md lists "the Rust crate version" among what verify-source
|
|
334
|
+
requires, but nothing read any Cargo.toml and okf-engine silently drifted
|
|
335
|
+
to 0.39.1 while the workspace published 0.45.0 (issue #172). rust-core
|
|
336
|
+
additionally pins okf-engine as an internal path dependency, so all three
|
|
337
|
+
numbers must move together.
|
|
338
|
+
"""
|
|
339
|
+
for crate_root, name in ((root / "okf-engine", "okf-engine"), (root / "rust-core", "okf-core")):
|
|
340
|
+
path = crate_root / "Cargo.toml"
|
|
341
|
+
try:
|
|
342
|
+
manifest = tomllib.loads(path.read_text(encoding="utf-8"))
|
|
343
|
+
except (OSError, UnicodeError, tomllib.TOMLDecodeError) as exc:
|
|
344
|
+
_fail(f"cannot read {path}: {exc}")
|
|
345
|
+
package = _mapping(manifest.get("package"), f"{path} [package]")
|
|
346
|
+
if _string(package, "version", str(path)) != version:
|
|
347
|
+
_fail(f"{name} crate version must be {version}")
|
|
348
|
+
dependencies = _mapping(manifest.get("dependencies"), "rust-core/Cargo.toml dependencies")
|
|
349
|
+
internal = _mapping(dependencies.get("okf-engine"), "rust-core okf-engine dependency")
|
|
350
|
+
if _string(internal, "version", "rust-core okf-engine dependency") != version:
|
|
351
|
+
_fail(f"rust-core internal okf-engine dependency must be {version}")
|
|
352
|
+
|
|
353
|
+
|
|
323
354
|
def verify_source(root: Path, tag: str | None = None) -> SourceContract:
|
|
324
355
|
"""Verify package names, versions, protocol, peer range and changelog."""
|
|
325
356
|
version = _project_version(root)
|
|
326
357
|
_verify_npm_contract(root, version)
|
|
327
358
|
_verify_protocol(root, version)
|
|
359
|
+
_verify_rust_crates(root, version)
|
|
328
360
|
changelog = _verify_changelog(root, version)
|
|
329
361
|
_verify_readme_action(root, version)
|
|
330
362
|
if tag is not None and tag != f"v{version}":
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""Relational inspection and validation for Open Knowledge Format bundles."""
|
|
2
2
|
|
|
3
3
|
from okf_parser.bundle import Bundle, validate_path
|
|
4
|
+
from okf_parser.concepts import concept, resolve_relations
|
|
4
5
|
from okf_parser.edit import EditError, preview_concept_edit, write_concept_edit
|
|
5
6
|
from okf_parser.engine import load_bundle
|
|
6
7
|
from okf_parser.graphql_adapter import (
|
|
@@ -29,10 +30,12 @@ __all__ = [
|
|
|
29
30
|
"ValidationReport",
|
|
30
31
|
"Violation",
|
|
31
32
|
"build_graphql_schema",
|
|
33
|
+
"concept",
|
|
32
34
|
"export_graphql_sdl",
|
|
33
35
|
"ingest_documents",
|
|
34
36
|
"load_bundle",
|
|
35
37
|
"preview_concept_edit",
|
|
38
|
+
"resolve_relations",
|
|
36
39
|
"validate_path",
|
|
37
40
|
"write_concept_edit",
|
|
38
41
|
]
|
|
@@ -12,6 +12,9 @@ cannot open on its own surfaces as DuckDB's own error, unchanged.
|
|
|
12
12
|
|
|
13
13
|
from __future__ import annotations
|
|
14
14
|
|
|
15
|
+
import hashlib
|
|
16
|
+
import hmac
|
|
17
|
+
import json
|
|
15
18
|
from io import StringIO
|
|
16
19
|
from pathlib import Path
|
|
17
20
|
from typing import TYPE_CHECKING, Literal
|
|
@@ -110,12 +113,88 @@ def _matches_candidate(destination: Path, candidate: str) -> bool:
|
|
|
110
113
|
return existing.parsed_digest == intended.parsed_digest
|
|
111
114
|
|
|
112
115
|
|
|
116
|
+
def _destination_state(destination: Path) -> dict[str, str]:
|
|
117
|
+
"""Return the destination state that must remain unchanged until commit."""
|
|
118
|
+
if destination.is_symlink():
|
|
119
|
+
return {"kind": "symlink", "target": str(destination.readlink())}
|
|
120
|
+
if destination.is_file():
|
|
121
|
+
return {
|
|
122
|
+
"kind": "file",
|
|
123
|
+
"sha256": hashlib.sha256(destination.read_bytes()).hexdigest(),
|
|
124
|
+
}
|
|
125
|
+
if destination.exists():
|
|
126
|
+
return {"kind": "other"}
|
|
127
|
+
return {"kind": "absent"}
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def _stable_rows(columns: Sequence[str], rows: Sequence[dict[str, object]]) -> list[list[object]]:
|
|
131
|
+
"""Preserve the effective source values in a JSON-stable representation."""
|
|
132
|
+
stable: list[list[object]] = []
|
|
133
|
+
for row in rows:
|
|
134
|
+
values: list[object] = []
|
|
135
|
+
for column in columns:
|
|
136
|
+
value = row.get(column)
|
|
137
|
+
if value is None:
|
|
138
|
+
values.append(None)
|
|
139
|
+
elif isinstance(value, str):
|
|
140
|
+
values.append(["str", value])
|
|
141
|
+
else:
|
|
142
|
+
values.append([type(value).__qualname__, str(value)])
|
|
143
|
+
stable.append(values)
|
|
144
|
+
return stable
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def _preview_token(
|
|
148
|
+
*,
|
|
149
|
+
source: str,
|
|
150
|
+
concept_type: str,
|
|
151
|
+
id_column: str | None,
|
|
152
|
+
overwrite: bool,
|
|
153
|
+
on_conflict: ImportConflictPolicy,
|
|
154
|
+
columns: Sequence[str],
|
|
155
|
+
rows: Sequence[dict[str, object]],
|
|
156
|
+
destinations: dict[str, dict[str, str]],
|
|
157
|
+
) -> str:
|
|
158
|
+
"""Bind an import preview to its source plan and current destination state."""
|
|
159
|
+
payload = {
|
|
160
|
+
"version": 1,
|
|
161
|
+
"source": source,
|
|
162
|
+
"concept_type": concept_type,
|
|
163
|
+
"id_column": id_column,
|
|
164
|
+
"overwrite": overwrite,
|
|
165
|
+
"on_conflict": on_conflict,
|
|
166
|
+
"columns": list(columns),
|
|
167
|
+
"rows": _stable_rows(columns, rows),
|
|
168
|
+
"destinations": [[relative, destinations[relative]] for relative in sorted(destinations)],
|
|
169
|
+
}
|
|
170
|
+
encoded = json.dumps(
|
|
171
|
+
payload,
|
|
172
|
+
ensure_ascii=False,
|
|
173
|
+
separators=(",", ":"),
|
|
174
|
+
sort_keys=True,
|
|
175
|
+
).encode("utf-8")
|
|
176
|
+
return hashlib.sha256(encoded).hexdigest()
|
|
177
|
+
|
|
178
|
+
|
|
113
179
|
def _validate_options(*, overwrite: bool, on_conflict: ImportConflictPolicy) -> None:
|
|
114
180
|
if overwrite and on_conflict == "verify-identical":
|
|
115
181
|
message = "overwrite and on_conflict='verify-identical' are mutually exclusive"
|
|
116
182
|
raise BundleImportError(message)
|
|
117
183
|
|
|
118
184
|
|
|
185
|
+
def _validate_preview_token(
|
|
186
|
+
*, write: bool, preview_token: str, expected_preview_token: str | None
|
|
187
|
+
) -> None:
|
|
188
|
+
"""Fail closed when a reviewed import preview no longer matches current state."""
|
|
189
|
+
if (
|
|
190
|
+
write
|
|
191
|
+
and expected_preview_token is not None
|
|
192
|
+
and not hmac.compare_digest(preview_token, expected_preview_token)
|
|
193
|
+
):
|
|
194
|
+
message = "import preview is stale; rerun preview before committing"
|
|
195
|
+
raise BundleImportError(message)
|
|
196
|
+
|
|
197
|
+
|
|
119
198
|
def import_bundle( # each argument is an independent public CLI flag.
|
|
120
199
|
source: str,
|
|
121
200
|
path: str,
|
|
@@ -125,6 +204,7 @@ def import_bundle( # each argument is an independent public CLI flag.
|
|
|
125
204
|
write: bool = False,
|
|
126
205
|
overwrite: bool = False,
|
|
127
206
|
on_conflict: ImportConflictPolicy = "skip",
|
|
207
|
+
expected_preview_token: str | None = None,
|
|
128
208
|
) -> dict[str, object]:
|
|
129
209
|
"""Materialize every row of a DuckDB-readable source as one concept document.
|
|
130
210
|
|
|
@@ -134,6 +214,11 @@ def import_bundle( # each argument is an independent public CLI flag.
|
|
|
134
214
|
concept value as an idempotent match and any divergence as an atomic
|
|
135
215
|
conflict. `overwrite` remains an explicit replacement policy and cannot be
|
|
136
216
|
combined with `verify-identical`.
|
|
217
|
+
|
|
218
|
+
Dry-runs return a deterministic ``preview_token``. When a write supplies
|
|
219
|
+
``expected_preview_token``, source values and every relevant destination
|
|
220
|
+
must still match that reviewed preview or the call fails before any
|
|
221
|
+
filesystem mutation.
|
|
137
222
|
"""
|
|
138
223
|
_validate_options(overwrite=overwrite, on_conflict=on_conflict)
|
|
139
224
|
root = Path(path).resolve()
|
|
@@ -142,6 +227,22 @@ def import_bundle( # each argument is an independent public CLI flag.
|
|
|
142
227
|
message = "source column 'type' is reserved; use --type to set concept identity"
|
|
143
228
|
raise BundleImportError(message)
|
|
144
229
|
plan, duplicate_ids = _plan(concept_type, columns, rows, id_column)
|
|
230
|
+
destinations = {relative: _destination_state(root / relative) for relative in plan}
|
|
231
|
+
preview_token = _preview_token(
|
|
232
|
+
source=source,
|
|
233
|
+
concept_type=concept_type,
|
|
234
|
+
id_column=id_column,
|
|
235
|
+
overwrite=overwrite,
|
|
236
|
+
on_conflict=on_conflict,
|
|
237
|
+
columns=columns,
|
|
238
|
+
rows=rows,
|
|
239
|
+
destinations=destinations,
|
|
240
|
+
)
|
|
241
|
+
_validate_preview_token(
|
|
242
|
+
write=write,
|
|
243
|
+
preview_token=preview_token,
|
|
244
|
+
expected_preview_token=expected_preview_token,
|
|
245
|
+
)
|
|
145
246
|
if duplicate_ids:
|
|
146
247
|
return {
|
|
147
248
|
"created": [],
|
|
@@ -150,6 +251,7 @@ def import_bundle( # each argument is an independent public CLI flag.
|
|
|
150
251
|
"matched_existing": [],
|
|
151
252
|
"conflicting_existing": [],
|
|
152
253
|
"duplicate_ids": list(duplicate_ids),
|
|
254
|
+
"preview_token": preview_token,
|
|
153
255
|
"written": False,
|
|
154
256
|
}
|
|
155
257
|
|
|
@@ -183,6 +285,7 @@ def import_bundle( # each argument is an independent public CLI flag.
|
|
|
183
285
|
"matched_existing": sorted(matched_existing),
|
|
184
286
|
"conflicting_existing": sorted(conflicting_existing),
|
|
185
287
|
"duplicate_ids": [],
|
|
288
|
+
"preview_token": preview_token,
|
|
186
289
|
"written": False,
|
|
187
290
|
}
|
|
188
291
|
|
|
@@ -194,6 +297,7 @@ def import_bundle( # each argument is an independent public CLI flag.
|
|
|
194
297
|
"matched_existing": sorted(matched_existing),
|
|
195
298
|
"conflicting_existing": [],
|
|
196
299
|
"duplicate_ids": [],
|
|
300
|
+
"preview_token": preview_token,
|
|
197
301
|
"written": False,
|
|
198
302
|
}
|
|
199
303
|
|
|
@@ -210,5 +314,6 @@ def import_bundle( # each argument is an independent public CLI flag.
|
|
|
210
314
|
"matched_existing": sorted(matched_existing),
|
|
211
315
|
"conflicting_existing": [],
|
|
212
316
|
"duplicate_ids": [],
|
|
317
|
+
"preview_token": preview_token,
|
|
213
318
|
"written": True,
|
|
214
319
|
}
|
|
@@ -99,6 +99,7 @@ def import_command( # each argument is an independent public CLI flag.
|
|
|
99
99
|
write: bool = False,
|
|
100
100
|
overwrite: bool = False,
|
|
101
101
|
on_conflict: ImportConflictPolicy = "skip",
|
|
102
|
+
expected_preview_token: str | None = None,
|
|
102
103
|
) -> CliResult[JsonPayload]:
|
|
103
104
|
"""Materialize every row of a DuckDB-readable source (CSV, Parquet, JSON) as a concept."""
|
|
104
105
|
payload = import_bundle(
|
|
@@ -109,6 +110,7 @@ def import_command( # each argument is an independent public CLI flag.
|
|
|
109
110
|
write=write,
|
|
110
111
|
overwrite=overwrite,
|
|
111
112
|
on_conflict=on_conflict,
|
|
113
|
+
expected_preview_token=expected_preview_token,
|
|
112
114
|
)
|
|
113
115
|
failed = bool(payload["duplicate_ids"]) or bool(payload["conflicting_existing"])
|
|
114
116
|
return CliResult(payload, 1 if failed else 0)
|
|
@@ -450,6 +452,7 @@ def mcp_import_write(
|
|
|
450
452
|
id_column: str | None = None,
|
|
451
453
|
overwrite: bool = False,
|
|
452
454
|
on_conflict: ImportConflictPolicy = "skip",
|
|
455
|
+
expected_preview_token: str | None = None,
|
|
453
456
|
) -> dict[str, object]:
|
|
454
457
|
"""Commit a tabular import using the existing import service."""
|
|
455
458
|
return import_bundle(
|
|
@@ -460,6 +463,7 @@ def mcp_import_write(
|
|
|
460
463
|
write=True,
|
|
461
464
|
overwrite=overwrite,
|
|
462
465
|
on_conflict=on_conflict,
|
|
466
|
+
expected_preview_token=expected_preview_token,
|
|
463
467
|
)
|
|
464
468
|
|
|
465
469
|
|