domainforge 0.13.0
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/.cargo/config.toml +6 -0
- package/.claude/settings.local.json +18 -0
- package/.coderabbit.yml +43 -0
- package/.codex/skills/release-management/SKILL.md +151 -0
- package/.codex/skills/release-management/agents/openai.yaml +4 -0
- package/.github/actions/decrypt-secrets/action.yml +121 -0
- package/.github/agents/Coder.agent.md +97 -0
- package/.github/agents/DeepResearch.agent.md +61 -0
- package/.github/chatmodes/tdd.vibepro.chatmode.md +1183 -0
- package/.github/copilot-instructions.md +13 -0
- package/.github/dependabot.yml +68 -0
- package/.github/workflows/README.md +165 -0
- package/.github/workflows/ci.yml +335 -0
- package/.github/workflows/dependabot-automerge.yml +114 -0
- package/.github/workflows/dependency-review.yml +27 -0
- package/.github/workflows/deploy.yml +87 -0
- package/.github/workflows/prepare-release.yml +168 -0
- package/.github/workflows/release-crates.yml +42 -0
- package/.github/workflows/release-npm.yml +137 -0
- package/.github/workflows/release-please.yml +29 -0
- package/.github/workflows/release-pypi.yml +96 -0
- package/.gitkeep +1 -0
- package/.release-please-manifest.json +5 -0
- package/.sea-registry.toml +10 -0
- package/.serena/project.yml +133 -0
- package/.sops.yaml +10 -0
- package/AGENTS.md +216 -0
- package/CHANGELOG.md +400 -0
- package/CLAUDE.md +62 -0
- package/CONTRIBUTING.md +323 -0
- package/Cargo.lock +3612 -0
- package/Cargo.toml +12 -0
- package/LICENSE +201 -0
- package/README.md +660 -0
- package/README_PYTHON.md +256 -0
- package/README_TYPESCRIPT.md +305 -0
- package/README_WASM.md +329 -0
- package/RELEASE_NOTES.md +41 -0
- package/bun.lock +378 -0
- package/bunfig.toml +11 -0
- package/check_output.txt +83 -0
- package/clippy_output.txt +80 -0
- package/commitlint.config.cjs +8 -0
- package/deny.toml +42 -0
- package/devbox.json +14 -0
- package/devbox.lock +76 -0
- package/docs/RELEASE_PROCESS.md +360 -0
- package/docs/diagnostics.md +161 -0
- package/docs/doc_guidelines.md +53 -0
- package/docs/explanations/README.md +21 -0
- package/docs/explanations/architecture-overview.md +109 -0
- package/docs/explanations/cross-language-binding-strategy.md +68 -0
- package/docs/explanations/graph-store-design.md +47 -0
- package/docs/explanations/performance-benchmarks.md +63 -0
- package/docs/explanations/policy-evaluation-logic.md +106 -0
- package/docs/explanations/semantic-modeling-concepts.md +109 -0
- package/docs/explanations/three-valued-logic.md +66 -0
- package/docs/explanations/versioning-strategy.md +45 -0
- package/docs/governance.md +168 -0
- package/docs/how-tos/README.md +46 -0
- package/docs/how-tos/ci-cd-validation.md +93 -0
- package/docs/how-tos/create-custom-units.md +125 -0
- package/docs/how-tos/define-policies.md +119 -0
- package/docs/how-tos/export-to-calm.md +110 -0
- package/docs/how-tos/export-to-protobuf.md +312 -0
- package/docs/how-tos/extend-grammar.md +133 -0
- package/docs/how-tos/generate-rdf-turtle.md +106 -0
- package/docs/how-tos/import-from-calm.md +114 -0
- package/docs/how-tos/import-from-sbvr.md +249 -0
- package/docs/how-tos/install-cli.md +126 -0
- package/docs/how-tos/parse-sea-files.md +132 -0
- package/docs/how-tos/policy-evaluation-modes.md +30 -0
- package/docs/how-tos/run-cross-language-tests.md +115 -0
- package/docs/how-tos/troubleshoot-napi-builds.md +55 -0
- package/docs/how-tos/use-modules-imports.md +285 -0
- package/docs/index.md +13 -0
- package/docs/plans/canonical-normalizer.md +121 -0
- package/docs/plans/cd_improvement.md +112 -0
- package/docs/plans/cli-ast.md +29 -0
- package/docs/plans/expression-bindings-and-normalizer-integration.md +174 -0
- package/docs/plans/protobuf_advanced_features_plan.md +597 -0
- package/docs/plans/protobuf_plan.yml +525 -0
- package/docs/plans/refactor_dsl_architecture.md +131 -0
- package/docs/plans/release-plan.md +163 -0
- package/docs/plans/sea_fmt_implementation_plan.md +516 -0
- package/docs/playbooks/README.md +18 -0
- package/docs/playbooks/adding-new-primitive.md +68 -0
- package/docs/playbooks/debugging-parser-failures.md +42 -0
- package/docs/playbooks/local-release-preparation.md +139 -0
- package/docs/playbooks/migrating-schema-versions.md +43 -0
- package/docs/playbooks/onboarding-contributors.md +64 -0
- package/docs/playbooks/releasing-beta.md +86 -0
- package/docs/playbooks/secret-management.md +64 -0
- package/docs/reference/README.md +199 -0
- package/docs/reference/ast-json-api.md +427 -0
- package/docs/reference/calm-mapping.md +519 -0
- package/docs/reference/cli-commands.md +588 -0
- package/docs/reference/configuration.md +202 -0
- package/docs/reference/error-codes.md +664 -0
- package/docs/reference/generated-artifacts-policy.md +53 -0
- package/docs/reference/grammar-spec.md +255 -0
- package/docs/reference/primitives-api.md +317 -0
- package/docs/reference/protobuf-api.md +426 -0
- package/docs/reference/python-api.md +485 -0
- package/docs/reference/registry.md +50 -0
- package/docs/reference/sea-dsl-ai-cheatsheet.yaml +913 -0
- package/docs/reference/security-model.md +74 -0
- package/docs/reference/typescript-api.md +508 -0
- package/docs/reference/wasm-api.md +420 -0
- package/docs/semantic-pack-review.md +144 -0
- package/docs/semantic-pack-signing.md +234 -0
- package/docs/semantic-packs.md +284 -0
- package/docs/specs/ADR-001-sea-dsl-semantic-source-of-truth.md +33 -0
- package/docs/specs/ADR-002-projection-first-class-construct.md +50 -0
- package/docs/specs/ADR-003-protobuf-projection-target.md +51 -0
- package/docs/specs/ADR-004-projection-compatibility-semantics.md +57 -0
- package/docs/specs/ADR-005-multi-language-support-strategy.md +112 -0
- package/docs/specs/ADR-006-error-handling-strategy.md +115 -0
- package/docs/specs/ADR-007-policy-evaluation-engine.md +95 -0
- package/docs/specs/ADR-008-knowledge-graph-integration.md +90 -0
- package/docs/specs/ADR-009-module-resolution-strategy.md +115 -0
- package/docs/specs/ADR-010-unit-system.md +106 -0
- package/docs/specs/PRD-001-sea-projection-framework.md +155 -0
- package/docs/specs/PRD-002-sea-cli-tooling.md +169 -0
- package/docs/specs/PRD-003-dsl-core-capabilities.md +275 -0
- package/docs/specs/README.md +62 -0
- package/docs/specs/SDS-001-protobuf-projection-engine.md +451 -0
- package/docs/specs/SDS-002-sea-core-architecture.md +268 -0
- package/docs/specs/SDS-003-parser-semantic-graph.md +377 -0
- package/docs/specs/SDS-004-policy-engine-design.md +362 -0
- package/docs/specs/SDS-005-knowledge-graph-module.md +364 -0
- package/docs/specs/SDS-006-calm-integration.md +367 -0
- package/docs/specs/SDS-007-sbvr-import.md +347 -0
- package/docs/templates/template_explanation.md +14 -0
- package/docs/templates/template_howto.md +21 -0
- package/docs/templates/template_playbook.md +21 -0
- package/docs/templates/template_reference.md +17 -0
- package/docs/templates/template_tutorial.md +24 -0
- package/docs/tutorials/README.md +12 -0
- package/docs/tutorials/first-sea-model.md +85 -0
- package/docs/tutorials/getting-started.md +98 -0
- package/docs/tutorials/python-binding-quickstart.md +107 -0
- package/docs/tutorials/typescript-binding-quickstart.md +91 -0
- package/docs/tutorials/wasm-in-browser.md +75 -0
- package/domainforge-core/CHANGELOG.md +138 -0
- package/domainforge-core/Cargo.toml +101 -0
- package/domainforge-core/MIGRATING.md +32 -0
- package/domainforge-core/README.md +197 -0
- package/domainforge-core/benchmark_results.txt +51 -0
- package/domainforge-core/build.rs +6 -0
- package/domainforge-core/deny.toml +31 -0
- package/domainforge-core/docs/specs/projections/sbvr_kg_mapping.md +43 -0
- package/domainforge-core/examples/basic.sea +7 -0
- package/domainforge-core/examples/cli/import_export_workflow.sh +38 -0
- package/domainforge-core/examples/cli/validate_example.sh +30 -0
- package/domainforge-core/examples/evolution_semantics.sea +31 -0
- package/domainforge-core/examples/parser_demo.rs +203 -0
- package/domainforge-core/grammar/sea.pest +408 -0
- package/domainforge-core/schemas/calm-v1.schema.json +170 -0
- package/domainforge-core/schemas/shacl/sea_shapes.ttl +19 -0
- package/domainforge-core/src/authority/compiler.rs +309 -0
- package/domainforge-core/src/authority/environment.rs +203 -0
- package/domainforge-core/src/authority/error.rs +164 -0
- package/domainforge-core/src/authority/fact_resolver.rs +224 -0
- package/domainforge-core/src/authority/mod.rs +25 -0
- package/domainforge-core/src/authority/pack.rs +133 -0
- package/domainforge-core/src/authority/policy.rs +224 -0
- package/domainforge-core/src/authority/resolver.rs +446 -0
- package/domainforge-core/src/authority/trace.rs +217 -0
- package/domainforge-core/src/authority/transform.rs +168 -0
- package/domainforge-core/src/authority/types.rs +617 -0
- package/domainforge-core/src/bin/domainforge.rs +25 -0
- package/domainforge-core/src/calm/export.rs +538 -0
- package/domainforge-core/src/calm/import.rs +1220 -0
- package/domainforge-core/src/calm/mod.rs +9 -0
- package/domainforge-core/src/calm/models.rs +108 -0
- package/domainforge-core/src/calm/sbvr_import.rs +9 -0
- package/domainforge-core/src/cli/authority.rs +149 -0
- package/domainforge-core/src/cli/format.rs +85 -0
- package/domainforge-core/src/cli/import.rs +133 -0
- package/domainforge-core/src/cli/mod.rs +64 -0
- package/domainforge-core/src/cli/normalize.rs +180 -0
- package/domainforge-core/src/cli/pack.rs +904 -0
- package/domainforge-core/src/cli/parse.rs +112 -0
- package/domainforge-core/src/cli/project.rs +294 -0
- package/domainforge-core/src/cli/registry.rs +41 -0
- package/domainforge-core/src/cli/test.rs +12 -0
- package/domainforge-core/src/cli/validate.rs +195 -0
- package/domainforge-core/src/cli/validate_kg.rs +80 -0
- package/domainforge-core/src/concept_id.rs +89 -0
- package/domainforge-core/src/error/diagnostics.rs +426 -0
- package/domainforge-core/src/error/fuzzy.rs +253 -0
- package/domainforge-core/src/error/mod.rs +13 -0
- package/domainforge-core/src/formatter/comments.rs +223 -0
- package/domainforge-core/src/formatter/config.rs +114 -0
- package/domainforge-core/src/formatter/mod.rs +22 -0
- package/domainforge-core/src/formatter/printer.rs +906 -0
- package/domainforge-core/src/graph/mod.rs +858 -0
- package/domainforge-core/src/graph/to_ast.rs +66 -0
- package/domainforge-core/src/kg.rs +1476 -0
- package/domainforge-core/src/kg_import.rs +251 -0
- package/domainforge-core/src/lib.rs +203 -0
- package/domainforge-core/src/module/mod.rs +1 -0
- package/domainforge-core/src/module/resolver.rs +260 -0
- package/domainforge-core/src/parser/ast.rs +2919 -0
- package/domainforge-core/src/parser/ast_convert.rs +494 -0
- package/domainforge-core/src/parser/ast_schema.rs +491 -0
- package/domainforge-core/src/parser/error.rs +291 -0
- package/domainforge-core/src/parser/lint.rs +39 -0
- package/domainforge-core/src/parser/mod.rs +193 -0
- package/domainforge-core/src/parser/printer.rs +702 -0
- package/domainforge-core/src/parser/profiles.rs +71 -0
- package/domainforge-core/src/parser/string_utils.rs +138 -0
- package/domainforge-core/src/patterns.rs +68 -0
- package/domainforge-core/src/policy/core.rs +1148 -0
- package/domainforge-core/src/policy/expression.rs +399 -0
- package/domainforge-core/src/policy/mod.rs +18 -0
- package/domainforge-core/src/policy/normalize.rs +1028 -0
- package/domainforge-core/src/policy/quantifier.rs +940 -0
- package/domainforge-core/src/policy/three_valued.rs +140 -0
- package/domainforge-core/src/policy/three_valued_microbench.rs +104 -0
- package/domainforge-core/src/policy/type_inference.rs +67 -0
- package/domainforge-core/src/policy/violation.rs +36 -0
- package/domainforge-core/src/primitives/concept_change.rs +61 -0
- package/domainforge-core/src/primitives/entity.rs +224 -0
- package/domainforge-core/src/primitives/flow.rs +111 -0
- package/domainforge-core/src/primitives/instance.rs +93 -0
- package/domainforge-core/src/primitives/mapping_contract.rs +50 -0
- package/domainforge-core/src/primitives/metric.rs +79 -0
- package/domainforge-core/src/primitives/mod.rs +25 -0
- package/domainforge-core/src/primitives/projection_contract.rs +50 -0
- package/domainforge-core/src/primitives/quantity.rs +56 -0
- package/domainforge-core/src/primitives/relation.rs +68 -0
- package/domainforge-core/src/primitives/resource.rs +237 -0
- package/domainforge-core/src/primitives/resource_instance.rs +88 -0
- package/domainforge-core/src/primitives/role.rs +49 -0
- package/domainforge-core/src/projection/buf.rs +404 -0
- package/domainforge-core/src/projection/contracts.rs +22 -0
- package/domainforge-core/src/projection/engine.rs +19 -0
- package/domainforge-core/src/projection/mod.rs +16 -0
- package/domainforge-core/src/projection/protobuf.rs +3331 -0
- package/domainforge-core/src/projection/registry.rs +43 -0
- package/domainforge-core/src/python/authority.rs +253 -0
- package/domainforge-core/src/python/error.rs +227 -0
- package/domainforge-core/src/python/formatter.rs +86 -0
- package/domainforge-core/src/python/graph.rs +366 -0
- package/domainforge-core/src/python/mod.rs +9 -0
- package/domainforge-core/src/python/policy.rs +651 -0
- package/domainforge-core/src/python/primitives.rs +796 -0
- package/domainforge-core/src/python/registry.rs +98 -0
- package/domainforge-core/src/python/semantic_pack.rs +619 -0
- package/domainforge-core/src/python/units.rs +96 -0
- package/domainforge-core/src/registry/mod.rs +432 -0
- package/domainforge-core/src/registry/tests.rs +210 -0
- package/domainforge-core/src/sbvr.rs +744 -0
- package/domainforge-core/src/semantic_pack/builder.rs +470 -0
- package/domainforge-core/src/semantic_pack/canonical_json.rs +184 -0
- package/domainforge-core/src/semantic_pack/diagnostics.rs +214 -0
- package/domainforge-core/src/semantic_pack/diff.rs +216 -0
- package/domainforge-core/src/semantic_pack/mod.rs +31 -0
- package/domainforge-core/src/semantic_pack/pack_set.rs +240 -0
- package/domainforge-core/src/semantic_pack/resolver.rs +437 -0
- package/domainforge-core/src/semantic_pack/review.rs +125 -0
- package/domainforge-core/src/semantic_pack/schema.rs +342 -0
- package/domainforge-core/src/semantic_pack/signing.rs +105 -0
- package/domainforge-core/src/semantic_pack/validator.rs +368 -0
- package/domainforge-core/src/semantic_version.rs +140 -0
- package/domainforge-core/src/test_utils.rs +12 -0
- package/domainforge-core/src/typescript/authority.rs +184 -0
- package/domainforge-core/src/typescript/error.rs +146 -0
- package/domainforge-core/src/typescript/formatter.rs +76 -0
- package/domainforge-core/src/typescript/graph.rs +391 -0
- package/domainforge-core/src/typescript/mod.rs +9 -0
- package/domainforge-core/src/typescript/policy.rs +564 -0
- package/domainforge-core/src/typescript/primitives.rs +784 -0
- package/domainforge-core/src/typescript/registry.rs +88 -0
- package/domainforge-core/src/typescript/semantic_pack.rs +470 -0
- package/domainforge-core/src/typescript/units.rs +76 -0
- package/domainforge-core/src/units/mod.rs +462 -0
- package/domainforge-core/src/uuid_module.rs +42 -0
- package/domainforge-core/src/validation_error.rs +818 -0
- package/domainforge-core/src/validation_result.rs +30 -0
- package/domainforge-core/src/wasm/authority.rs +192 -0
- package/domainforge-core/src/wasm/error.rs +145 -0
- package/domainforge-core/src/wasm/formatter.rs +69 -0
- package/domainforge-core/src/wasm/graph.rs +471 -0
- package/domainforge-core/src/wasm/mod.rs +16 -0
- package/domainforge-core/src/wasm/policy.rs +607 -0
- package/domainforge-core/src/wasm/primitives.rs +295 -0
- package/domainforge-core/src/wasm/semantic_pack.rs +471 -0
- package/domainforge-core/src/wasm/units.rs +62 -0
- package/domainforge-core/std/aws.sea +6 -0
- package/domainforge-core/std/core.sea +6 -0
- package/domainforge-core/std/http.sea +27 -0
- package/domainforge-core/tests/aggregation_enhanced_tests.rs +162 -0
- package/domainforge-core/tests/aggregation_eval_tests.rs +248 -0
- package/domainforge-core/tests/aggregation_integration_tests.rs +379 -0
- package/domainforge-core/tests/aggregation_parser_tests.rs +92 -0
- package/domainforge-core/tests/aggregation_tests.rs +102 -0
- package/domainforge-core/tests/authority_conformance_tests.rs +1173 -0
- package/domainforge-core/tests/calm_round_trip_tests.rs +283 -0
- package/domainforge-core/tests/calm_schema_validation_tests.rs +137 -0
- package/domainforge-core/tests/cast_operator_tests.rs +85 -0
- package/domainforge-core/tests/cli_binary_check.rs +37 -0
- package/domainforge-core/tests/cli_import_tests.rs +291 -0
- package/domainforge-core/tests/cli_path_traversal_tests.rs +124 -0
- package/domainforge-core/tests/cli_tests.rs +63 -0
- package/domainforge-core/tests/diagnostics_tests.rs +203 -0
- package/domainforge-core/tests/dimension_unit_tests.rs +80 -0
- package/domainforge-core/tests/entity_tests.rs +69 -0
- package/domainforge-core/tests/evolution_semantics_tests.rs +157 -0
- package/domainforge-core/tests/flow_tests.rs +78 -0
- package/domainforge-core/tests/flow_unit_validation_tests.rs +31 -0
- package/domainforge-core/tests/graph_integration_tests.rs +218 -0
- package/domainforge-core/tests/graph_tests.rs +626 -0
- package/domainforge-core/tests/import_parsing_tests.rs +23 -0
- package/domainforge-core/tests/instance_integration_tests.rs +98 -0
- package/domainforge-core/tests/instance_parsing_tests.rs +58 -0
- package/domainforge-core/tests/instance_tests.rs +61 -0
- package/domainforge-core/tests/kg_uri_encoding_tests.rs +53 -0
- package/domainforge-core/tests/lint_tests.rs +19 -0
- package/domainforge-core/tests/metric_tests.rs +143 -0
- package/domainforge-core/tests/module_resolution_tests.rs +100 -0
- package/domainforge-core/tests/namespace_registry_tests.rs +247 -0
- package/domainforge-core/tests/null_handling_tests.rs +26 -0
- package/domainforge-core/tests/parser_ast_v3.rs +53 -0
- package/domainforge-core/tests/parser_dimension_registry_tests.rs +20 -0
- package/domainforge-core/tests/parser_integration_tests.rs +294 -0
- package/domainforge-core/tests/parser_metadata_tests.rs +97 -0
- package/domainforge-core/tests/parser_resource_domain_only_graph_test.rs +21 -0
- package/domainforge-core/tests/parser_resource_limits_tests.rs +122 -0
- package/domainforge-core/tests/parser_tests.rs +512 -0
- package/domainforge-core/tests/pattern_semantics_tests.rs +87 -0
- package/domainforge-core/tests/phase_14_determinism_tests.rs +166 -0
- package/domainforge-core/tests/phase_15_validation_error_tests.rs +136 -0
- package/domainforge-core/tests/phase_16_unicode_tests.rs +248 -0
- package/domainforge-core/tests/phase_17_export_tests.rs +285 -0
- package/domainforge-core/tests/phase_17_round_trip_tests.rs +264 -0
- package/domainforge-core/tests/policy_tests.rs +635 -0
- package/domainforge-core/tests/primitives_integration_tests.rs +151 -0
- package/domainforge-core/tests/print_rdf_xml.rs +14 -0
- package/domainforge-core/tests/printer_tests.rs +204 -0
- package/domainforge-core/tests/profile_tests.rs +35 -0
- package/domainforge-core/tests/projection_contracts_tests.rs +154 -0
- package/domainforge-core/tests/protobuf_projection_tests.rs +199 -0
- package/domainforge-core/tests/quantity_tests.rs +41 -0
- package/domainforge-core/tests/rdf_xml_typed_literal_tests.rs +105 -0
- package/domainforge-core/tests/registry_schema_tests.rs +33 -0
- package/domainforge-core/tests/resource_tests.rs +50 -0
- package/domainforge-core/tests/resource_unit_tests.rs +24 -0
- package/domainforge-core/tests/roles_relations_tests.rs +61 -0
- package/domainforge-core/tests/round_trip_tests.rs +34 -0
- package/domainforge-core/tests/runtime_toggle_tests.rs +70 -0
- package/domainforge-core/tests/sbvr_fact_schema_tests.rs +60 -0
- package/domainforge-core/tests/sbvr_flow_facts_tests.rs +55 -0
- package/domainforge-core/tests/sbvr_parsing_tests.rs +53 -0
- package/domainforge-core/tests/semantic_pack_alias_resolution.rs +197 -0
- package/domainforge-core/tests/semantic_pack_build.rs +302 -0
- package/domainforge-core/tests/semantic_pack_consumer_smoke.rs +150 -0
- package/domainforge-core/tests/semantic_pack_pack_set.rs +160 -0
- package/domainforge-core/tests/semantic_pack_signing.rs +157 -0
- package/domainforge-core/tests/semantic_pack_three_valued.rs +250 -0
- package/domainforge-core/tests/semantic_pack_validate.rs +196 -0
- package/domainforge-core/tests/std_lib_tests.rs +37 -0
- package/domainforge-core/tests/temporal_evaluation_tests.rs +159 -0
- package/domainforge-core/tests/temporal_semantics_tests.rs +214 -0
- package/domainforge-core/tests/three_valued_quantifiers_tests.rs +164 -0
- package/domainforge-core/tests/turtle_entity_export_tests.rs +38 -0
- package/domainforge-core/tests/turtle_escaping_tests.rs +53 -0
- package/domainforge-core/tests/turtle_resource_export_tests.rs +34 -0
- package/domainforge-core/tests/type_inference_tests.rs +40 -0
- package/domainforge-core/tests/unicode_validation_tests.rs +169 -0
- package/domainforge-core/tests/unit_tests.rs +81 -0
- package/domainforge-core/tests/validate_tests.rs +38 -0
- package/domainforge-core/tests/validation_unit_mismatch_tests.rs +83 -0
- package/domainforge-core/tests/wasm_tests.rs +229 -0
- package/domainforge-python/CHANGELOG-python.md +12 -0
- package/domainforge-python/MIGRATING.md +24 -0
- package/domainforge-python/README.md +256 -0
- package/domainforge-python/domainforge/__init__.py +95 -0
- package/domainforge-python/domainforge/domainforge.pyi +519 -0
- package/domainforge-python/pyproject.toml +36 -0
- package/domainforge-typescript/CHANGELOG-typescript.md +12 -0
- package/domainforge-typescript/LICENSE +201 -0
- package/domainforge-typescript/MIGRATING.md +24 -0
- package/domainforge-typescript/README.md +305 -0
- package/domainforge-typescript/index.d.ts +452 -0
- package/domainforge-typescript/index.js +361 -0
- package/domainforge-typescript/package.json +60 -0
- package/example.js +61 -0
- package/examples/browser.html +366 -0
- package/examples/namespaces/finance/cashflow.sea +5 -0
- package/examples/namespaces/logistics/core.sea +7 -0
- package/examples/observability_metrics.sea +38 -0
- package/fixtures/semantic_packs/acme_procurement/domain/entities.sea +39 -0
- package/fixtures/semantic_packs/acme_procurement/domain/metrics.sea +11 -0
- package/fixtures/semantic_packs/acme_procurement/domain/relations.sea +7 -0
- package/fixtures/semantic_packs/acme_procurement/domain/resources.sea +9 -0
- package/fixtures/semantic_packs/acme_procurement/review/acme.procurement.semantic-review.jsonl +7 -0
- package/fixtures/semantic_packs/acme_procurement/tests/ambiguous_vendor_alias.sea +8 -0
- package/fixtures/semantic_packs/acme_procurement/tests/deprecated_vendor_alias.sea +8 -0
- package/fixtures/semantic_packs/acme_procurement/tests/invalid_relation.sea +3 -0
- package/fixtures/semantic_packs/acme_procurement/tests/proposed_concept.sea +8 -0
- package/fixtures/semantic_packs/acme_procurement/tests/rejected_concept.sea +8 -0
- package/fixtures/semantic_packs/acme_procurement/tests/unit_mismatch.sea +7 -0
- package/fixtures/semantic_packs/acme_procurement/tests/unknown_vendor_policy.sea +8 -0
- package/fixtures/semantic_packs/acme_procurement/tests/valid_purchase_policy.sea +8 -0
- package/index.d.ts +2 -0
- package/index.js +8 -0
- package/justfile +200 -0
- package/lefthook.yml +13 -0
- package/lib/validate_native_exports.d.ts +4 -0
- package/lib/validate_native_exports.js +12 -0
- package/package.json +22 -0
- package/pytest.ini +5 -0
- package/python/tests/test_registry.py +75 -0
- package/python/tests/test_units.py +18 -0
- package/release-please-config.json +49 -0
- package/requirements-dev.txt +3 -0
- package/requirements.txt +3 -0
- package/rust-toolchain.toml +3 -0
- package/schemas/ast-v1.schema.json +72 -0
- package/schemas/ast-v2.schema.json +1200 -0
- package/schemas/ast-v3.schema.json +1200 -0
- package/schemas/sea-registry.schema.json +45 -0
- package/scripts/build-python.sh +37 -0
- package/scripts/build-release.sh +279 -0
- package/scripts/build-typescript.sh +13 -0
- package/scripts/build-wasm.sh +113 -0
- package/scripts/bump-version.sh +245 -0
- package/scripts/check_unused_test_imports.py +85 -0
- package/scripts/ci_tasks.py +379 -0
- package/scripts/clear_debug_test.sh +10 -0
- package/scripts/create-github-release.sh +262 -0
- package/scripts/create-tag.sh +203 -0
- package/scripts/find_and_link_test_binary.sh +70 -0
- package/scripts/generate-changelog.sh +271 -0
- package/scripts/generate-release-notes.sh +205 -0
- package/scripts/lint_release_security.py +96 -0
- package/scripts/lint_release_workflows.py +82 -0
- package/scripts/lint_workflow_gates.py +113 -0
- package/scripts/optimized-wasm-build.sh +61 -0
- package/scripts/patch_napi_types.py +62 -0
- package/scripts/pre-release-check.sh +289 -0
- package/scripts/prepare_rust_debug.sh +52 -0
- package/scripts/release.sh +373 -0
- package/scripts/resolve_rust_binary.py +230 -0
- package/scripts/run_commitlint.sh +29 -0
- package/scripts/test-all.sh +77 -0
- package/scripts/update_launch_program.py +93 -0
- package/secrets/README.md +27 -0
- package/secrets/secrets.yaml +21 -0
- package/test_integration.py +67 -0
- package/tests/test_authority.py +328 -0
- package/tests/test_ci_tasks.py +143 -0
- package/tests/test_expression.py +256 -0
- package/tests/test_golden_payment_flow.py +42 -0
- package/tests/test_graph.py +127 -0
- package/tests/test_instance.py +136 -0
- package/tests/test_parser.py +82 -0
- package/tests/test_primitives.py +68 -0
- package/tests/test_role_relation_parity.py +56 -0
- package/tests/test_runtime_toggle.py +156 -0
- package/tests/test_semantic_pack.py +639 -0
- package/tests/test_three_valued_eval.py +159 -0
- package/tsconfig.json +30 -0
- package/typescript-tests/advanced.test.ts +165 -0
- package/typescript-tests/authority.test.ts +216 -0
- package/typescript-tests/expression.test.ts +228 -0
- package/typescript-tests/golden-payment-flow.test.ts +51 -0
- package/typescript-tests/graph.test.ts +142 -0
- package/typescript-tests/native-binding.test.ts +20 -0
- package/typescript-tests/primitives.test.ts +88 -0
- package/typescript-tests/registry.test.ts +122 -0
- package/typescript-tests/role_relation.test.ts +63 -0
- package/typescript-tests/runtime_toggle.test.ts +141 -0
- package/typescript-tests/semantic-pack.test.ts +556 -0
- package/typescript-tests/three_valued_eval.test.ts +135 -0
- package/typescript-tests/units.test.ts +36 -0
- package/vitest.config.ts +13 -0
- package/wasm_demo.html +225 -0
|
@@ -0,0 +1,858 @@
|
|
|
1
|
+
use crate::patterns::Pattern;
|
|
2
|
+
use crate::policy::{Policy, Severity, Violation};
|
|
3
|
+
use crate::primitives::{
|
|
4
|
+
ConceptChange, Entity, Flow, Instance, MappingContract, Metric, ProjectionContract,
|
|
5
|
+
RelationType, Resource, ResourceInstance, Role,
|
|
6
|
+
};
|
|
7
|
+
use crate::validation_result::ValidationResult;
|
|
8
|
+
use crate::ConceptId;
|
|
9
|
+
use indexmap::IndexMap;
|
|
10
|
+
use serde::{Deserialize, Serialize};
|
|
11
|
+
|
|
12
|
+
pub mod to_ast;
|
|
13
|
+
|
|
14
|
+
/// Configuration for graph evaluation behavior
|
|
15
|
+
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
16
|
+
pub struct GraphConfig {
|
|
17
|
+
/// Enable three-valued logic (True, False, NULL) for policy evaluation.
|
|
18
|
+
/// When false, uses strict boolean logic (True, False).
|
|
19
|
+
pub use_three_valued_logic: bool,
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
impl Default for GraphConfig {
|
|
23
|
+
fn default() -> Self {
|
|
24
|
+
Self {
|
|
25
|
+
// Default to three-valued logic for backward compatibility with the feature flag
|
|
26
|
+
use_three_valued_logic: true,
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
|
32
|
+
pub struct Graph {
|
|
33
|
+
entities: IndexMap<ConceptId, Entity>,
|
|
34
|
+
roles: IndexMap<ConceptId, Role>,
|
|
35
|
+
resources: IndexMap<ConceptId, Resource>,
|
|
36
|
+
flows: IndexMap<ConceptId, Flow>,
|
|
37
|
+
relations: IndexMap<ConceptId, RelationType>,
|
|
38
|
+
instances: IndexMap<ConceptId, ResourceInstance>,
|
|
39
|
+
/// Entity instances keyed by ConceptId for consistency with other graph collections.
|
|
40
|
+
entity_instances: IndexMap<ConceptId, Instance>,
|
|
41
|
+
policies: IndexMap<ConceptId, Policy>,
|
|
42
|
+
#[serde(default)]
|
|
43
|
+
patterns: IndexMap<ConceptId, Pattern>,
|
|
44
|
+
#[serde(default)]
|
|
45
|
+
concept_changes: IndexMap<ConceptId, ConceptChange>,
|
|
46
|
+
#[serde(default)]
|
|
47
|
+
metrics: IndexMap<ConceptId, Metric>,
|
|
48
|
+
#[serde(default)]
|
|
49
|
+
mappings: IndexMap<ConceptId, MappingContract>,
|
|
50
|
+
#[serde(default)]
|
|
51
|
+
projections: IndexMap<ConceptId, ProjectionContract>,
|
|
52
|
+
#[serde(default)]
|
|
53
|
+
entity_roles: IndexMap<ConceptId, Vec<ConceptId>>,
|
|
54
|
+
#[serde(default)]
|
|
55
|
+
config: GraphConfig,
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
impl Graph {
|
|
59
|
+
pub fn new() -> Self {
|
|
60
|
+
Self::default()
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
pub fn is_empty(&self) -> bool {
|
|
64
|
+
self.entities.is_empty()
|
|
65
|
+
&& self.roles.is_empty()
|
|
66
|
+
&& self.resources.is_empty()
|
|
67
|
+
&& self.flows.is_empty()
|
|
68
|
+
&& self.relations.is_empty()
|
|
69
|
+
&& self.instances.is_empty()
|
|
70
|
+
&& self.entity_instances.is_empty()
|
|
71
|
+
&& self.policies.is_empty()
|
|
72
|
+
&& self.patterns.is_empty()
|
|
73
|
+
&& self.concept_changes.is_empty()
|
|
74
|
+
&& self.concept_changes.is_empty()
|
|
75
|
+
&& self.metrics.is_empty()
|
|
76
|
+
&& self.mappings.is_empty()
|
|
77
|
+
&& self.projections.is_empty()
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/// Set the evaluation mode for policy evaluation.
|
|
81
|
+
/// When `use_three_valued_logic` is true, policies will use three-valued logic (True, False, NULL).
|
|
82
|
+
/// When false, policies will use strict boolean logic (True, False).
|
|
83
|
+
pub fn set_evaluation_mode(&mut self, use_three_valued_logic: bool) {
|
|
84
|
+
self.config.use_three_valued_logic = use_three_valued_logic;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/// Get the current evaluation mode.
|
|
88
|
+
pub fn use_three_valued_logic(&self) -> bool {
|
|
89
|
+
self.config.use_three_valued_logic
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
pub fn config(&self) -> &GraphConfig {
|
|
93
|
+
&self.config
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
pub fn config_mut(&mut self) -> &mut GraphConfig {
|
|
97
|
+
&mut self.config
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
pub fn entity_count(&self) -> usize {
|
|
101
|
+
self.entities.len()
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
pub fn add_entity(&mut self, entity: Entity) -> Result<(), String> {
|
|
105
|
+
let id = entity.id().clone();
|
|
106
|
+
if self.entities.contains_key(&id) {
|
|
107
|
+
return Err(format!("Entity with ID {} already exists", id));
|
|
108
|
+
}
|
|
109
|
+
self.entities.insert(id, entity);
|
|
110
|
+
Ok(())
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
pub fn has_entity(&self, id: &ConceptId) -> bool {
|
|
114
|
+
self.entities.contains_key(id)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
pub fn get_entity(&self, id: &ConceptId) -> Option<&Entity> {
|
|
118
|
+
self.entities.get(id)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/// Returns a mutable reference to an Entity identified by `id`.
|
|
122
|
+
/// This method is necessary for operations that need to update attributes
|
|
123
|
+
/// on existing entities (such as association relationships).
|
|
124
|
+
pub fn get_entity_mut(&mut self, id: &ConceptId) -> Option<&mut Entity> {
|
|
125
|
+
self.entities.get_mut(id)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
pub fn remove_entity(&mut self, id: &ConceptId) -> Result<Entity, String> {
|
|
129
|
+
// Check for references in flows
|
|
130
|
+
let referencing_flows: Vec<String> = self
|
|
131
|
+
.flows
|
|
132
|
+
.values()
|
|
133
|
+
.filter(|flow| flow.from_id() == id || flow.to_id() == id)
|
|
134
|
+
.map(|flow| flow.id().to_string())
|
|
135
|
+
.collect();
|
|
136
|
+
|
|
137
|
+
// Check for references in instances
|
|
138
|
+
let referencing_instances: Vec<String> = self
|
|
139
|
+
.instances
|
|
140
|
+
.values()
|
|
141
|
+
.filter(|instance| instance.entity_id() == id)
|
|
142
|
+
.map(|instance| instance.id().to_string())
|
|
143
|
+
.collect();
|
|
144
|
+
|
|
145
|
+
if !referencing_flows.is_empty() || !referencing_instances.is_empty() {
|
|
146
|
+
let mut error_msg = format!("Cannot remove entity {} because it is referenced", id);
|
|
147
|
+
if !referencing_flows.is_empty() {
|
|
148
|
+
error_msg.push_str(&format!(" by flows: {}", referencing_flows.join(", ")));
|
|
149
|
+
}
|
|
150
|
+
if !referencing_instances.is_empty() {
|
|
151
|
+
error_msg.push_str(&format!(
|
|
152
|
+
" by instances: {}",
|
|
153
|
+
referencing_instances.join(", ")
|
|
154
|
+
));
|
|
155
|
+
}
|
|
156
|
+
return Err(error_msg);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
self.entities
|
|
160
|
+
.shift_remove(id)
|
|
161
|
+
.ok_or_else(|| format!("Entity with ID {} not found", id))
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
pub fn role_count(&self) -> usize {
|
|
165
|
+
self.roles.len()
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
pub fn add_role(&mut self, role: Role) -> Result<(), String> {
|
|
169
|
+
let id = role.id().clone();
|
|
170
|
+
if self.roles.contains_key(&id) {
|
|
171
|
+
return Err(format!("Role with ID {} already exists", id));
|
|
172
|
+
}
|
|
173
|
+
self.roles.insert(id, role);
|
|
174
|
+
Ok(())
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
pub fn get_role(&self, id: &ConceptId) -> Option<&Role> {
|
|
178
|
+
self.roles.get(id)
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
pub fn has_role(&self, id: &ConceptId) -> bool {
|
|
182
|
+
self.roles.contains_key(id)
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
pub fn assign_role_to_entity(
|
|
186
|
+
&mut self,
|
|
187
|
+
entity_id: ConceptId,
|
|
188
|
+
role_id: ConceptId,
|
|
189
|
+
) -> Result<(), String> {
|
|
190
|
+
if !self.entities.contains_key(&entity_id) {
|
|
191
|
+
return Err(format!("Entity with ID {} not found", entity_id));
|
|
192
|
+
}
|
|
193
|
+
if !self.roles.contains_key(&role_id) {
|
|
194
|
+
return Err(format!("Role with ID {} not found", role_id));
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
let roles = self.entity_roles.entry(entity_id).or_default();
|
|
198
|
+
if !roles.contains(&role_id) {
|
|
199
|
+
roles.push(role_id);
|
|
200
|
+
}
|
|
201
|
+
Ok(())
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
pub fn roles_for_entity(&self, entity_id: &ConceptId) -> Option<&Vec<ConceptId>> {
|
|
205
|
+
self.entity_roles.get(entity_id)
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
pub fn role_names_for_entity(&self, entity_id: &ConceptId) -> Vec<String> {
|
|
209
|
+
self.entity_roles
|
|
210
|
+
.get(entity_id)
|
|
211
|
+
.into_iter()
|
|
212
|
+
.flat_map(|roles| roles.iter())
|
|
213
|
+
.filter_map(|role_id| self.roles.get(role_id))
|
|
214
|
+
.map(|role| role.name().to_string())
|
|
215
|
+
.collect()
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
pub fn resource_count(&self) -> usize {
|
|
219
|
+
self.resources.len()
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
pub fn add_resource(&mut self, resource: Resource) -> Result<(), String> {
|
|
223
|
+
let id = resource.id().clone();
|
|
224
|
+
if self.resources.contains_key(&id) {
|
|
225
|
+
return Err(format!("Resource with ID {} already exists", id));
|
|
226
|
+
}
|
|
227
|
+
self.resources.insert(id, resource);
|
|
228
|
+
Ok(())
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
pub fn has_resource(&self, id: &ConceptId) -> bool {
|
|
232
|
+
self.resources.contains_key(id)
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
pub fn get_resource(&self, id: &ConceptId) -> Option<&Resource> {
|
|
236
|
+
self.resources.get(id)
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
pub fn remove_resource(&mut self, id: &ConceptId) -> Result<Resource, String> {
|
|
240
|
+
// Check for references in flows
|
|
241
|
+
let referencing_flows: Vec<String> = self
|
|
242
|
+
.flows
|
|
243
|
+
.values()
|
|
244
|
+
.filter(|flow| flow.resource_id() == id)
|
|
245
|
+
.map(|flow| flow.id().to_string())
|
|
246
|
+
.collect();
|
|
247
|
+
|
|
248
|
+
// Check for references in instances
|
|
249
|
+
let referencing_instances: Vec<String> = self
|
|
250
|
+
.instances
|
|
251
|
+
.values()
|
|
252
|
+
.filter(|instance| instance.resource_id() == id)
|
|
253
|
+
.map(|instance| instance.id().to_string())
|
|
254
|
+
.collect();
|
|
255
|
+
|
|
256
|
+
if !referencing_flows.is_empty() || !referencing_instances.is_empty() {
|
|
257
|
+
let mut error_msg = format!("Cannot remove resource {} because it is referenced", id);
|
|
258
|
+
if !referencing_flows.is_empty() {
|
|
259
|
+
error_msg.push_str(&format!(" by flows: {}", referencing_flows.join(", ")));
|
|
260
|
+
}
|
|
261
|
+
if !referencing_instances.is_empty() {
|
|
262
|
+
error_msg.push_str(&format!(
|
|
263
|
+
" by instances: {}",
|
|
264
|
+
referencing_instances.join(", ")
|
|
265
|
+
));
|
|
266
|
+
}
|
|
267
|
+
return Err(error_msg);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
self.resources
|
|
271
|
+
.shift_remove(id)
|
|
272
|
+
.ok_or_else(|| format!("Resource with ID {} not found", id))
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
pub fn flow_count(&self) -> usize {
|
|
276
|
+
self.flows.len()
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
pub fn pattern_count(&self) -> usize {
|
|
280
|
+
self.patterns.len()
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
pub fn relation_count(&self) -> usize {
|
|
284
|
+
self.relations.len()
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
pub fn add_relation_type(&mut self, relation: RelationType) -> Result<(), String> {
|
|
288
|
+
let id = relation.id().clone();
|
|
289
|
+
if self.relations.contains_key(&id) {
|
|
290
|
+
return Err(format!("Relation with ID {} already exists", id));
|
|
291
|
+
}
|
|
292
|
+
self.relations.insert(id, relation);
|
|
293
|
+
Ok(())
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
pub fn all_relations(&self) -> Vec<&RelationType> {
|
|
297
|
+
self.relations.values().collect()
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
pub fn add_flow(&mut self, flow: Flow) -> Result<(), String> {
|
|
301
|
+
let id = flow.id().clone();
|
|
302
|
+
if self.flows.contains_key(&id) {
|
|
303
|
+
return Err(format!("Flow with ID {} already exists", id));
|
|
304
|
+
}
|
|
305
|
+
if !self.entities.contains_key(flow.from_id()) {
|
|
306
|
+
return Err("Source entity not found".to_string());
|
|
307
|
+
}
|
|
308
|
+
if !self.entities.contains_key(flow.to_id()) {
|
|
309
|
+
return Err("Target entity not found".to_string());
|
|
310
|
+
}
|
|
311
|
+
if !self.resources.contains_key(flow.resource_id()) {
|
|
312
|
+
return Err("Resource not found".to_string());
|
|
313
|
+
}
|
|
314
|
+
self.flows.insert(id, flow);
|
|
315
|
+
Ok(())
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
pub fn add_pattern(&mut self, pattern: Pattern) -> Result<(), String> {
|
|
319
|
+
let id = pattern.id().clone();
|
|
320
|
+
if self.patterns.contains_key(&id) {
|
|
321
|
+
return Err(format!("Pattern with ID {} already exists", id));
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
if self.patterns.values().any(|existing| {
|
|
325
|
+
existing.name() == pattern.name() && existing.namespace() == pattern.namespace()
|
|
326
|
+
}) {
|
|
327
|
+
return Err(format!(
|
|
328
|
+
"Pattern '{}' already declared in namespace '{}'",
|
|
329
|
+
pattern.name(),
|
|
330
|
+
pattern.namespace()
|
|
331
|
+
));
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
self.patterns.insert(id, pattern);
|
|
335
|
+
Ok(())
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
pub fn add_concept_change(&mut self, change: ConceptChange) -> Result<(), String> {
|
|
339
|
+
let id = change.id().clone();
|
|
340
|
+
if self.concept_changes.contains_key(&id) {
|
|
341
|
+
return Err(format!("ConceptChange with ID {} already exists", id));
|
|
342
|
+
}
|
|
343
|
+
self.concept_changes.insert(id, change);
|
|
344
|
+
Ok(())
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
pub fn get_concept_change(&self, id: &ConceptId) -> Option<&ConceptChange> {
|
|
348
|
+
self.concept_changes.get(id)
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
pub fn all_concept_changes(&self) -> Vec<&ConceptChange> {
|
|
352
|
+
self.concept_changes.values().collect()
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
pub fn has_flow(&self, id: &ConceptId) -> bool {
|
|
356
|
+
self.flows.contains_key(id)
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
pub fn get_flow(&self, id: &ConceptId) -> Option<&Flow> {
|
|
360
|
+
self.flows.get(id)
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
pub fn remove_flow(&mut self, id: &ConceptId) -> Result<Flow, String> {
|
|
364
|
+
self.flows
|
|
365
|
+
.shift_remove(id)
|
|
366
|
+
.ok_or_else(|| format!("Flow with ID {} not found", id))
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
pub fn instance_count(&self) -> usize {
|
|
370
|
+
self.instances.len()
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
pub fn add_instance(&mut self, instance: ResourceInstance) -> Result<(), String> {
|
|
374
|
+
let id = instance.id().clone();
|
|
375
|
+
if self.instances.contains_key(&id) {
|
|
376
|
+
return Err(format!("Instance with ID {} already exists", id));
|
|
377
|
+
}
|
|
378
|
+
if !self.entities.contains_key(instance.entity_id()) {
|
|
379
|
+
return Err("Entity not found".to_string());
|
|
380
|
+
}
|
|
381
|
+
if !self.resources.contains_key(instance.resource_id()) {
|
|
382
|
+
return Err("Resource not found".to_string());
|
|
383
|
+
}
|
|
384
|
+
self.instances.insert(id, instance);
|
|
385
|
+
Ok(())
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
/// Adds an association relationship from `owner` -> `owned` using a named `rel_type`.
|
|
389
|
+
/// Associations are represented as a JSON attribute `associations` on the source entity to
|
|
390
|
+
/// maintain a simple, serializable representation without introducing a new primitive.
|
|
391
|
+
pub fn add_association(
|
|
392
|
+
&mut self,
|
|
393
|
+
owner: &ConceptId,
|
|
394
|
+
owned: &ConceptId,
|
|
395
|
+
rel_type: &str,
|
|
396
|
+
) -> Result<(), String> {
|
|
397
|
+
if !self.entities.contains_key(owner) {
|
|
398
|
+
return Err("Source entity not found".to_string());
|
|
399
|
+
}
|
|
400
|
+
if !self.entities.contains_key(owned) {
|
|
401
|
+
return Err("Destination entity not found".to_string());
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
let owned_str = owned.to_string();
|
|
405
|
+
let rel_type_str = rel_type.to_string();
|
|
406
|
+
|
|
407
|
+
// Mutably borrow the entity and insert/update the `associations` attribute.
|
|
408
|
+
if let Some(entity) = self.entities.get_mut(owner) {
|
|
409
|
+
use serde_json::{json, Value};
|
|
410
|
+
let mut associations: Value = entity
|
|
411
|
+
.get_attribute("associations")
|
|
412
|
+
.cloned()
|
|
413
|
+
.unwrap_or_else(|| json!([]));
|
|
414
|
+
|
|
415
|
+
if let Value::Array(arr) = &mut associations {
|
|
416
|
+
arr.push(json!({"type": rel_type_str, "target": owned_str}));
|
|
417
|
+
} else {
|
|
418
|
+
// Replace non-array associations with an array structure.
|
|
419
|
+
associations = json!([ {"type": rel_type_str, "target": owned_str} ]);
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
entity.set_attribute("associations", associations);
|
|
423
|
+
Ok(())
|
|
424
|
+
} else {
|
|
425
|
+
Err("Failed to retrieve entity for association".to_string())
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
pub fn has_instance(&self, id: &ConceptId) -> bool {
|
|
430
|
+
self.instances.contains_key(id)
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
pub fn get_instance(&self, id: &ConceptId) -> Option<&ResourceInstance> {
|
|
434
|
+
self.instances.get(id)
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
pub fn remove_instance(&mut self, id: &ConceptId) -> Result<ResourceInstance, String> {
|
|
438
|
+
self.instances
|
|
439
|
+
.shift_remove(id)
|
|
440
|
+
.ok_or_else(|| format!("Instance with ID {} not found", id))
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
// Entity Instance methods
|
|
444
|
+
pub fn entity_instance_count(&self) -> usize {
|
|
445
|
+
self.entity_instances.len()
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
pub fn add_entity_instance(&mut self, instance: Instance) -> Result<(), String> {
|
|
449
|
+
let id = instance.id().clone();
|
|
450
|
+
if self.entity_instances.contains_key(&id) {
|
|
451
|
+
return Err(format!(
|
|
452
|
+
"Entity instance '{}' already exists",
|
|
453
|
+
instance.name()
|
|
454
|
+
));
|
|
455
|
+
}
|
|
456
|
+
if self.find_entity_instance_by_name(instance.name()).is_some() {
|
|
457
|
+
return Err(format!(
|
|
458
|
+
"Entity instance '{}' already exists",
|
|
459
|
+
instance.name()
|
|
460
|
+
));
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
let namespace = instance.namespace();
|
|
464
|
+
let entity_type = instance.entity_type();
|
|
465
|
+
|
|
466
|
+
self.find_entity_by_name_and_namespace(entity_type, namespace)
|
|
467
|
+
.ok_or_else(|| {
|
|
468
|
+
format!(
|
|
469
|
+
"Entity '{}' not found in namespace '{}'",
|
|
470
|
+
entity_type, namespace
|
|
471
|
+
)
|
|
472
|
+
})?;
|
|
473
|
+
|
|
474
|
+
self.entity_instances.insert(id, instance);
|
|
475
|
+
Ok(())
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
pub fn get_entity_instance(&self, name: &str) -> Option<&Instance> {
|
|
479
|
+
self.find_entity_instance_by_name(name)
|
|
480
|
+
.and_then(|id| self.entity_instances.get(&id))
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
pub fn get_entity_instance_mut(&mut self, name: &str) -> Option<&mut Instance> {
|
|
484
|
+
let id = self.find_entity_instance_by_name(name)?;
|
|
485
|
+
self.entity_instances.get_mut(&id)
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
pub fn all_entity_instances(&self) -> Vec<&Instance> {
|
|
489
|
+
self.entity_instances.values().collect()
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
pub fn remove_entity_instance(&mut self, name: &str) -> Result<Instance, String> {
|
|
493
|
+
let id = self
|
|
494
|
+
.find_entity_instance_by_name(name)
|
|
495
|
+
.ok_or_else(|| format!("Entity instance '{}' not found", name))?;
|
|
496
|
+
|
|
497
|
+
self.entity_instances
|
|
498
|
+
.shift_remove(&id)
|
|
499
|
+
.ok_or_else(|| format!("Entity instance '{}' not found", name))
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
pub fn has_entity_instance_by_id(&self, id: &ConceptId) -> bool {
|
|
503
|
+
self.entity_instances.contains_key(id)
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
pub fn get_entity_instance_by_id(&self, id: &ConceptId) -> Option<&Instance> {
|
|
507
|
+
self.entity_instances.get(id)
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
pub fn remove_entity_instance_by_id(&mut self, id: &ConceptId) -> Result<Instance, String> {
|
|
511
|
+
self.entity_instances
|
|
512
|
+
.shift_remove(id)
|
|
513
|
+
.ok_or_else(|| format!("Entity instance with id '{}' not found", id))
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
pub fn flows_from(&self, entity_id: &ConceptId) -> Vec<&Flow> {
|
|
517
|
+
self.flows
|
|
518
|
+
.values()
|
|
519
|
+
.filter(|flow| flow.from_id() == entity_id)
|
|
520
|
+
.collect()
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
pub fn flows_to(&self, entity_id: &ConceptId) -> Vec<&Flow> {
|
|
524
|
+
self.flows
|
|
525
|
+
.values()
|
|
526
|
+
.filter(|flow| flow.to_id() == entity_id)
|
|
527
|
+
.collect()
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
pub fn upstream_entities(&self, entity_id: &ConceptId) -> Vec<&Entity> {
|
|
531
|
+
self.flows_to(entity_id)
|
|
532
|
+
.iter()
|
|
533
|
+
.filter_map(|flow| self.get_entity(flow.from_id()))
|
|
534
|
+
.collect()
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
pub fn downstream_entities(&self, entity_id: &ConceptId) -> Vec<&Entity> {
|
|
538
|
+
self.flows_from(entity_id)
|
|
539
|
+
.iter()
|
|
540
|
+
.filter_map(|flow| self.get_entity(flow.to_id()))
|
|
541
|
+
.collect()
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
pub fn find_entity_by_name_and_namespace(
|
|
545
|
+
&self,
|
|
546
|
+
name: &str,
|
|
547
|
+
namespace: &str,
|
|
548
|
+
) -> Option<ConceptId> {
|
|
549
|
+
self.entities
|
|
550
|
+
.iter()
|
|
551
|
+
.find(|(_, entity)| entity.name() == name && entity.namespace() == namespace)
|
|
552
|
+
.map(|(id, _)| id.clone())
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
pub fn find_entity_by_name(&self, name: &str) -> Option<ConceptId> {
|
|
556
|
+
self.entities
|
|
557
|
+
.iter()
|
|
558
|
+
.find(|(_, entity)| entity.name() == name)
|
|
559
|
+
.map(|(id, _)| id.clone())
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
pub fn find_resource_by_name(&self, name: &str) -> Option<ConceptId> {
|
|
563
|
+
self.resources
|
|
564
|
+
.iter()
|
|
565
|
+
.find(|(_, resource)| resource.name() == name)
|
|
566
|
+
.map(|(id, _)| id.clone())
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
pub fn find_role_by_name(&self, name: &str) -> Option<ConceptId> {
|
|
570
|
+
self.roles
|
|
571
|
+
.iter()
|
|
572
|
+
.find(|(_, role)| role.name() == name)
|
|
573
|
+
.map(|(id, _)| id.clone())
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
pub fn find_entity_instance_by_name(&self, name: &str) -> Option<ConceptId> {
|
|
577
|
+
self.entity_instances
|
|
578
|
+
.iter()
|
|
579
|
+
.find(|(_, instance)| instance.name() == name)
|
|
580
|
+
.map(|(id, _)| id.clone())
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
pub fn find_entity_instance_by_name_and_namespace(
|
|
584
|
+
&self,
|
|
585
|
+
name: &str,
|
|
586
|
+
namespace: &str,
|
|
587
|
+
) -> Option<ConceptId> {
|
|
588
|
+
self.entity_instances
|
|
589
|
+
.iter()
|
|
590
|
+
.find(|(_, instance)| instance.name() == name && instance.namespace() == namespace)
|
|
591
|
+
.map(|(id, _)| id.clone())
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
pub fn find_pattern(&self, name: &str, namespace: Option<&str>) -> Option<&Pattern> {
|
|
595
|
+
if let Some(ns) = namespace {
|
|
596
|
+
if let Some((_, pattern)) = self
|
|
597
|
+
.patterns
|
|
598
|
+
.iter()
|
|
599
|
+
.find(|(_, pattern)| pattern.name() == name && pattern.namespace() == ns)
|
|
600
|
+
{
|
|
601
|
+
return Some(pattern);
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
self.patterns
|
|
606
|
+
.iter()
|
|
607
|
+
.find(|(_, pattern)| pattern.name() == name)
|
|
608
|
+
.map(|(_, pattern)| pattern)
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
pub fn all_entities(&self) -> Vec<&Entity> {
|
|
612
|
+
self.entities.values().collect()
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
pub fn all_roles(&self) -> Vec<&Role> {
|
|
616
|
+
self.roles.values().collect()
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
pub fn all_resources(&self) -> Vec<&Resource> {
|
|
620
|
+
self.resources.values().collect()
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
pub fn all_flows(&self) -> Vec<&Flow> {
|
|
624
|
+
self.flows.values().collect()
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
pub fn all_instances(&self) -> Vec<&ResourceInstance> {
|
|
628
|
+
self.instances.values().collect()
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
pub fn all_patterns(&self) -> Vec<&Pattern> {
|
|
632
|
+
self.patterns.values().collect()
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
pub fn policy_count(&self) -> usize {
|
|
636
|
+
self.policies.len()
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
pub fn add_policy(&mut self, policy: Policy) -> Result<(), String> {
|
|
640
|
+
let id = policy.id.clone();
|
|
641
|
+
if self.policies.contains_key(&id) {
|
|
642
|
+
return Err(format!("Policy with ID {} already exists", id));
|
|
643
|
+
}
|
|
644
|
+
self.policies.insert(id, policy);
|
|
645
|
+
Ok(())
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
pub fn has_policy(&self, id: &ConceptId) -> bool {
|
|
649
|
+
self.policies.contains_key(id)
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
pub fn get_policy(&self, id: &ConceptId) -> Option<&Policy> {
|
|
653
|
+
self.policies.get(id)
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
pub fn remove_policy(&mut self, id: &ConceptId) -> Result<Policy, String> {
|
|
657
|
+
self.policies
|
|
658
|
+
.shift_remove(id)
|
|
659
|
+
.ok_or_else(|| format!("Policy with ID {} not found", id))
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
pub fn all_policies(&self) -> Vec<&Policy> {
|
|
663
|
+
self.policies.values().collect()
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
pub fn metric_count(&self) -> usize {
|
|
667
|
+
self.metrics.len()
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
pub fn add_metric(&mut self, metric: Metric) -> Result<(), String> {
|
|
671
|
+
let id = metric.id().clone();
|
|
672
|
+
if self.metrics.contains_key(&id) {
|
|
673
|
+
return Err(format!("Metric with ID {} already exists", id));
|
|
674
|
+
}
|
|
675
|
+
self.metrics.insert(id, metric);
|
|
676
|
+
Ok(())
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
pub fn has_metric(&self, id: &ConceptId) -> bool {
|
|
680
|
+
self.metrics.contains_key(id)
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
pub fn get_metric(&self, id: &ConceptId) -> Option<&Metric> {
|
|
684
|
+
self.metrics.get(id)
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
pub fn all_metrics(&self) -> Vec<&Metric> {
|
|
688
|
+
self.metrics.values().collect()
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
pub fn mapping_count(&self) -> usize {
|
|
692
|
+
self.mappings.len()
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
pub fn add_mapping(&mut self, mapping: MappingContract) -> Result<(), String> {
|
|
696
|
+
let id = mapping.id().clone();
|
|
697
|
+
if self.mappings.contains_key(&id) {
|
|
698
|
+
return Err(format!("Mapping with ID {} already exists", id));
|
|
699
|
+
}
|
|
700
|
+
self.mappings.insert(id, mapping);
|
|
701
|
+
Ok(())
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
pub fn has_mapping(&self, id: &ConceptId) -> bool {
|
|
705
|
+
self.mappings.contains_key(id)
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
pub fn get_mapping(&self, id: &ConceptId) -> Option<&MappingContract> {
|
|
709
|
+
self.mappings.get(id)
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
pub fn all_mappings(&self) -> Vec<&MappingContract> {
|
|
713
|
+
self.mappings.values().collect()
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
pub fn projection_count(&self) -> usize {
|
|
717
|
+
self.projections.len()
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
pub fn add_projection(&mut self, projection: ProjectionContract) -> Result<(), String> {
|
|
721
|
+
let id = projection.id().clone();
|
|
722
|
+
if self.projections.contains_key(&id) {
|
|
723
|
+
return Err(format!("Projection with ID {} already exists", id));
|
|
724
|
+
}
|
|
725
|
+
self.projections.insert(id, projection);
|
|
726
|
+
Ok(())
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
pub fn has_projection(&self, id: &ConceptId) -> bool {
|
|
730
|
+
self.projections.contains_key(id)
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
pub fn get_projection(&self, id: &ConceptId) -> Option<&ProjectionContract> {
|
|
734
|
+
self.projections.get(id)
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
pub fn all_projections(&self) -> Vec<&ProjectionContract> {
|
|
738
|
+
self.projections.values().collect()
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
/// Extend this graph with all nodes and policies from another graph.
|
|
742
|
+
/// The operation is atomic: the existing graph is only modified if the entire
|
|
743
|
+
/// merge succeeds, which prevents partial state when errors occur.
|
|
744
|
+
pub fn extend(&mut self, other: Graph) -> Result<(), String> {
|
|
745
|
+
let mut merged = self.clone();
|
|
746
|
+
merged.extend_from_graph(other)?;
|
|
747
|
+
*self = merged;
|
|
748
|
+
Ok(())
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
fn extend_from_graph(&mut self, other: Graph) -> Result<(), String> {
|
|
752
|
+
let Graph {
|
|
753
|
+
entities,
|
|
754
|
+
roles,
|
|
755
|
+
resources,
|
|
756
|
+
flows,
|
|
757
|
+
relations,
|
|
758
|
+
instances,
|
|
759
|
+
entity_instances,
|
|
760
|
+
policies,
|
|
761
|
+
patterns,
|
|
762
|
+
concept_changes,
|
|
763
|
+
metrics,
|
|
764
|
+
mappings,
|
|
765
|
+
projections,
|
|
766
|
+
entity_roles,
|
|
767
|
+
config: _,
|
|
768
|
+
} = other;
|
|
769
|
+
|
|
770
|
+
for entity in entities.into_values() {
|
|
771
|
+
self.add_entity(entity)?;
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
for role in roles.into_values() {
|
|
775
|
+
self.add_role(role)?;
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
for resource in resources.into_values() {
|
|
779
|
+
self.add_resource(resource)?;
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
for instance in instances.into_values() {
|
|
783
|
+
self.add_instance(instance)?;
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
for entity_instance in entity_instances.into_values() {
|
|
787
|
+
self.add_entity_instance(entity_instance)?;
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
for flow in flows.into_values() {
|
|
791
|
+
self.add_flow(flow)?;
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
for relation in relations.into_values() {
|
|
795
|
+
self.add_relation_type(relation)?;
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
for pattern in patterns.into_values() {
|
|
799
|
+
self.add_pattern(pattern)?;
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
for (entity_id, roles_for_entity) in entity_roles.into_iter() {
|
|
803
|
+
for role_id in roles_for_entity {
|
|
804
|
+
self.assign_role_to_entity(entity_id.clone(), role_id)?;
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
for policy in policies.into_values() {
|
|
809
|
+
self.add_policy(policy)?;
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
for change in concept_changes.into_values() {
|
|
813
|
+
self.add_concept_change(change)?;
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
for metric in metrics.into_values() {
|
|
817
|
+
self.add_metric(metric)?;
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
for mapping in mappings.into_values() {
|
|
821
|
+
self.add_mapping(mapping)?;
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
for projection in projections.into_values() {
|
|
825
|
+
self.add_projection(projection)?;
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
Ok(())
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
/// Validate the graph by evaluating all policies against it and
|
|
832
|
+
/// collecting any violations produced. Returns a `ValidationResult`.
|
|
833
|
+
pub fn validate(&self) -> ValidationResult {
|
|
834
|
+
let mut all_violations: Vec<Violation> = Vec::new();
|
|
835
|
+
let use_three_valued_logic = self.config.use_three_valued_logic;
|
|
836
|
+
|
|
837
|
+
for policy in self.policies.values() {
|
|
838
|
+
match policy.evaluate_with_mode(self, use_three_valued_logic) {
|
|
839
|
+
Ok(eval) => {
|
|
840
|
+
all_violations.extend(eval.violations);
|
|
841
|
+
}
|
|
842
|
+
Err(err) => {
|
|
843
|
+
// If a policy evaluation fails, produce an ERROR severity
|
|
844
|
+
// violation indicating evaluation failure so the user can
|
|
845
|
+
// see the issue.
|
|
846
|
+
let v = Violation::new(
|
|
847
|
+
&policy.name,
|
|
848
|
+
format!("Policy evaluation failed: {}", err),
|
|
849
|
+
Severity::Error,
|
|
850
|
+
);
|
|
851
|
+
all_violations.push(v);
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
ValidationResult::new(self.policies.len(), all_violations)
|
|
857
|
+
}
|
|
858
|
+
}
|