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,253 @@
|
|
|
1
|
+
//! Fuzzy string matching for "did you mean?" suggestions.
|
|
2
|
+
//!
|
|
3
|
+
//! Implements the Levenshtein distance algorithm (Wagner-Fischer) to provide
|
|
4
|
+
//! helpful suggestions when users make typos in entity names, resource names,
|
|
5
|
+
//! or other identifiers.
|
|
6
|
+
/// Calculate the Levenshtein distance between two strings
|
|
7
|
+
///
|
|
8
|
+
/// Uses the Wagner-Fischer dynamic programming algorithm with O(mn) time complexity
|
|
9
|
+
/// and O(min(m,n)) space complexity.
|
|
10
|
+
///
|
|
11
|
+
/// # Examples
|
|
12
|
+
///
|
|
13
|
+
/// ```
|
|
14
|
+
/// use domainforge_core::error::fuzzy::levenshtein_distance;
|
|
15
|
+
///
|
|
16
|
+
/// assert_eq!(levenshtein_distance("kitten", "sitting"), 3);
|
|
17
|
+
/// assert_eq!(levenshtein_distance("Saturday", "Sunday"), 3);
|
|
18
|
+
/// assert_eq!(levenshtein_distance("", "abc"), 3);
|
|
19
|
+
/// ```
|
|
20
|
+
pub fn levenshtein_distance(a: &str, b: &str) -> usize {
|
|
21
|
+
let a_len = a.chars().count();
|
|
22
|
+
let b_len = b.chars().count();
|
|
23
|
+
|
|
24
|
+
if a_len == 0 {
|
|
25
|
+
return b_len;
|
|
26
|
+
}
|
|
27
|
+
if b_len == 0 {
|
|
28
|
+
return a_len;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Use the shorter string for the columns to minimize space
|
|
32
|
+
let (short, long, short_len) = if a_len < b_len {
|
|
33
|
+
(a, b, a_len)
|
|
34
|
+
} else {
|
|
35
|
+
(b, a, b_len)
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
// We only need two rows for the dynamic programming table
|
|
39
|
+
let mut prev_row: Vec<usize> = (0..=short_len).collect();
|
|
40
|
+
let mut curr_row: Vec<usize> = vec![0; short_len + 1];
|
|
41
|
+
|
|
42
|
+
let short_chars: Vec<char> = short.chars().collect();
|
|
43
|
+
|
|
44
|
+
for (i, long_char) in long.chars().enumerate() {
|
|
45
|
+
curr_row[0] = i + 1;
|
|
46
|
+
|
|
47
|
+
for (j, &short_char) in short_chars.iter().enumerate() {
|
|
48
|
+
let cost = if long_char == short_char { 0 } else { 1 };
|
|
49
|
+
|
|
50
|
+
curr_row[j + 1] = std::cmp::min(
|
|
51
|
+
std::cmp::min(
|
|
52
|
+
curr_row[j] + 1, // insertion
|
|
53
|
+
prev_row[j + 1] + 1, // deletion
|
|
54
|
+
),
|
|
55
|
+
prev_row[j] + cost, // substitution
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
std::mem::swap(&mut prev_row, &mut curr_row);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
prev_row[short_len]
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/// Find similar strings within a given edit distance threshold
|
|
66
|
+
///
|
|
67
|
+
/// Returns all candidates that are within `threshold` edits of the target string,
|
|
68
|
+
/// sorted by distance (closest first).
|
|
69
|
+
///
|
|
70
|
+
/// # Arguments
|
|
71
|
+
///
|
|
72
|
+
/// * `target` - The string to match against
|
|
73
|
+
/// * `candidates` - Potential matches to consider
|
|
74
|
+
/// * `threshold` - Maximum edit distance to include (default recommendation: 2)
|
|
75
|
+
///
|
|
76
|
+
/// # Examples
|
|
77
|
+
///
|
|
78
|
+
/// ```
|
|
79
|
+
/// use domainforge_core::error::fuzzy::suggest_similar;
|
|
80
|
+
///
|
|
81
|
+
/// let candidates = vec![
|
|
82
|
+
/// "Warehouse".to_string(),
|
|
83
|
+
/// "Factory".to_string(),
|
|
84
|
+
/// "Supplier".to_string(),
|
|
85
|
+
/// ];
|
|
86
|
+
///
|
|
87
|
+
/// let suggestions = suggest_similar("Warehous", &candidates, 2);
|
|
88
|
+
/// assert_eq!(suggestions, vec!["Warehouse"]);
|
|
89
|
+
/// ```
|
|
90
|
+
pub fn suggest_similar(target: &str, candidates: &[String], threshold: usize) -> Vec<String> {
|
|
91
|
+
let mut matches: Vec<(String, usize)> = candidates
|
|
92
|
+
.iter()
|
|
93
|
+
.filter_map(|candidate| {
|
|
94
|
+
let distance = levenshtein_distance(target, candidate);
|
|
95
|
+
if distance <= threshold {
|
|
96
|
+
Some((candidate.clone(), distance))
|
|
97
|
+
} else {
|
|
98
|
+
None
|
|
99
|
+
}
|
|
100
|
+
})
|
|
101
|
+
.collect();
|
|
102
|
+
|
|
103
|
+
// Sort by distance (closest first), then alphabetically for ties
|
|
104
|
+
matches.sort_by(|a, b| a.1.cmp(&b.1).then(a.0.cmp(&b.0)));
|
|
105
|
+
|
|
106
|
+
matches.into_iter().map(|(s, _)| s).collect()
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/// Find the single best match for a target string
|
|
110
|
+
///
|
|
111
|
+
/// Returns the closest match if it's within the threshold, otherwise None.
|
|
112
|
+
/// If multiple candidates have the same distance, returns the first alphabetically.
|
|
113
|
+
///
|
|
114
|
+
/// # Arguments
|
|
115
|
+
///
|
|
116
|
+
/// * `target` - The string to match against
|
|
117
|
+
/// * `candidates` - Potential matches to consider
|
|
118
|
+
/// * `threshold` - Maximum edit distance to accept (default recommendation: 2)
|
|
119
|
+
///
|
|
120
|
+
/// # Examples
|
|
121
|
+
///
|
|
122
|
+
/// ```
|
|
123
|
+
/// use domainforge_core::error::fuzzy::find_best_match;
|
|
124
|
+
///
|
|
125
|
+
/// let candidates = vec![
|
|
126
|
+
/// "Warehouse".to_string(),
|
|
127
|
+
/// "Factory".to_string(),
|
|
128
|
+
/// ];
|
|
129
|
+
///
|
|
130
|
+
/// assert_eq!(find_best_match("Warehous", &candidates, 2), Some("Warehouse".to_string()));
|
|
131
|
+
/// assert_eq!(find_best_match("XYZ", &candidates, 2), None);
|
|
132
|
+
/// ```
|
|
133
|
+
pub fn find_best_match(target: &str, candidates: &[String], threshold: usize) -> Option<String> {
|
|
134
|
+
suggest_similar(target, candidates, threshold)
|
|
135
|
+
.into_iter()
|
|
136
|
+
.next()
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
#[cfg(test)]
|
|
140
|
+
mod tests {
|
|
141
|
+
use super::*;
|
|
142
|
+
|
|
143
|
+
#[test]
|
|
144
|
+
fn test_levenshtein_distance_identical() {
|
|
145
|
+
assert_eq!(levenshtein_distance("hello", "hello"), 0);
|
|
146
|
+
assert_eq!(levenshtein_distance("", ""), 0);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
#[test]
|
|
150
|
+
fn test_levenshtein_distance_empty() {
|
|
151
|
+
assert_eq!(levenshtein_distance("", "abc"), 3);
|
|
152
|
+
assert_eq!(levenshtein_distance("abc", ""), 3);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
#[test]
|
|
156
|
+
fn test_levenshtein_distance_single_char() {
|
|
157
|
+
assert_eq!(levenshtein_distance("a", "b"), 1);
|
|
158
|
+
assert_eq!(levenshtein_distance("a", "a"), 0);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
#[test]
|
|
162
|
+
fn test_levenshtein_distance_classic_examples() {
|
|
163
|
+
assert_eq!(levenshtein_distance("kitten", "sitting"), 3);
|
|
164
|
+
assert_eq!(levenshtein_distance("Saturday", "Sunday"), 3);
|
|
165
|
+
assert_eq!(levenshtein_distance("rosettacode", "raisethysword"), 8);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
#[test]
|
|
169
|
+
fn test_levenshtein_distance_case_sensitive() {
|
|
170
|
+
assert_eq!(levenshtein_distance("Hello", "hello"), 1);
|
|
171
|
+
assert_eq!(levenshtein_distance("HELLO", "hello"), 5);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
#[test]
|
|
175
|
+
fn test_suggest_similar_exact_match() {
|
|
176
|
+
let candidates = vec!["Warehouse".to_string(), "Factory".to_string()];
|
|
177
|
+
let suggestions = suggest_similar("Warehouse", &candidates, 2);
|
|
178
|
+
assert_eq!(suggestions, vec!["Warehouse"]);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
#[test]
|
|
182
|
+
fn test_suggest_similar_typo() {
|
|
183
|
+
let candidates = vec![
|
|
184
|
+
"Warehouse".to_string(),
|
|
185
|
+
"Factory".to_string(),
|
|
186
|
+
"Supplier".to_string(),
|
|
187
|
+
];
|
|
188
|
+
let suggestions = suggest_similar("Warehous", &candidates, 2);
|
|
189
|
+
assert_eq!(suggestions, vec!["Warehouse"]);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
#[test]
|
|
193
|
+
fn test_suggest_similar_multiple_matches() {
|
|
194
|
+
let candidates = vec![
|
|
195
|
+
"Warehouse".to_string(),
|
|
196
|
+
"Warehouses".to_string(),
|
|
197
|
+
"Factory".to_string(),
|
|
198
|
+
];
|
|
199
|
+
let suggestions = suggest_similar("Warehous", &candidates, 2);
|
|
200
|
+
// Both Warehouse and Warehouses are within threshold
|
|
201
|
+
assert!(suggestions.contains(&"Warehouse".to_string()));
|
|
202
|
+
assert!(suggestions.contains(&"Warehouses".to_string()));
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
#[test]
|
|
206
|
+
fn test_suggest_similar_no_matches() {
|
|
207
|
+
let candidates = vec!["Warehouse".to_string(), "Factory".to_string()];
|
|
208
|
+
let suggestions = suggest_similar("XYZ", &candidates, 2);
|
|
209
|
+
assert_eq!(suggestions.len(), 0);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
#[test]
|
|
213
|
+
fn test_suggest_similar_sorted_by_distance() {
|
|
214
|
+
let candidates = vec![
|
|
215
|
+
"Warehouse".to_string(),
|
|
216
|
+
"Warehouses".to_string(),
|
|
217
|
+
"Ware".to_string(),
|
|
218
|
+
];
|
|
219
|
+
let suggestions = suggest_similar("War", &candidates, 5);
|
|
220
|
+
// "Ware" (dist 1), "Warehouse" (dist 6), "Warehouses" (dist 7).
|
|
221
|
+
// With threshold 5, only "Ware" is expected.
|
|
222
|
+
assert_eq!(suggestions, vec!["Ware"]);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
#[test]
|
|
226
|
+
fn test_find_best_match() {
|
|
227
|
+
let candidates = vec![
|
|
228
|
+
"Warehouse".to_string(),
|
|
229
|
+
"Factory".to_string(),
|
|
230
|
+
"Supplier".to_string(),
|
|
231
|
+
];
|
|
232
|
+
assert_eq!(
|
|
233
|
+
find_best_match("Warehous", &candidates, 2),
|
|
234
|
+
Some("Warehouse".to_string())
|
|
235
|
+
);
|
|
236
|
+
assert_eq!(find_best_match("XYZ", &candidates, 2), None);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
#[test]
|
|
240
|
+
fn test_find_best_match_tie_alphabetical() {
|
|
241
|
+
let candidates = vec!["Apple".to_string(), "Apples".to_string()];
|
|
242
|
+
// Both are distance 1 from "Appl", should return alphabetically first
|
|
243
|
+
let result = find_best_match("Appl", &candidates, 2);
|
|
244
|
+
assert_eq!(result, Some("Apple".to_string()));
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
#[test]
|
|
248
|
+
fn test_unicode_support() {
|
|
249
|
+
assert_eq!(levenshtein_distance("café", "cafe"), 1);
|
|
250
|
+
assert_eq!(levenshtein_distance("北京", "北京"), 0);
|
|
251
|
+
assert_eq!(levenshtein_distance("北京", "上海"), 2);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/// Error handling and diagnostics module
|
|
2
|
+
///
|
|
3
|
+
/// This module provides comprehensive error handling infrastructure including:
|
|
4
|
+
/// - Error codes for all validation errors
|
|
5
|
+
/// - Source position and range tracking
|
|
6
|
+
/// - Fuzzy string matching for "did you mean?" suggestions
|
|
7
|
+
/// - Multiple diagnostic output formatters (JSON, Human-readable, LSP)
|
|
8
|
+
pub mod diagnostics;
|
|
9
|
+
pub mod fuzzy;
|
|
10
|
+
|
|
11
|
+
// Re-export commonly used types
|
|
12
|
+
pub use crate::validation_error::{ErrorCode, Position, SourceRange, ValidationError};
|
|
13
|
+
pub use diagnostics::{DiagnosticFormatter, HumanFormatter, JsonFormatter, LspFormatter};
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
//! Comment extraction and preservation utilities.
|
|
2
|
+
//!
|
|
3
|
+
//! This module handles extracting comments from source code and associating
|
|
4
|
+
//! them with their corresponding declarations.
|
|
5
|
+
|
|
6
|
+
use std::collections::BTreeMap;
|
|
7
|
+
|
|
8
|
+
/// A comment extracted from source code.
|
|
9
|
+
#[derive(Debug, Clone, PartialEq)]
|
|
10
|
+
pub struct Comment {
|
|
11
|
+
/// The comment text (without the leading //)
|
|
12
|
+
pub text: String,
|
|
13
|
+
/// Line number (1-indexed)
|
|
14
|
+
pub line: usize,
|
|
15
|
+
/// Whether this is a trailing comment (on same line as code)
|
|
16
|
+
pub is_trailing: bool,
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/// Extracts comments from source code, preserving their positions.
|
|
20
|
+
///
|
|
21
|
+
/// Returns a map of line numbers to comments, where the line number
|
|
22
|
+
/// indicates where the comment appears in the source.
|
|
23
|
+
pub fn extract_comments(source: &str) -> BTreeMap<usize, Vec<Comment>> {
|
|
24
|
+
let mut comments: BTreeMap<usize, Vec<Comment>> = BTreeMap::new();
|
|
25
|
+
|
|
26
|
+
for (line_idx, line) in source.lines().enumerate() {
|
|
27
|
+
let line_num = line_idx + 1;
|
|
28
|
+
let trimmed = line.trim();
|
|
29
|
+
|
|
30
|
+
// Check for line comment
|
|
31
|
+
if let Some(comment_start) = trimmed.find("//") {
|
|
32
|
+
let before_comment = &line[..line.find("//").unwrap_or(0)];
|
|
33
|
+
let is_trailing = !before_comment.trim().is_empty();
|
|
34
|
+
|
|
35
|
+
let comment_text = trimmed[comment_start + 2..].trim();
|
|
36
|
+
|
|
37
|
+
comments.entry(line_num).or_default().push(Comment {
|
|
38
|
+
text: comment_text.to_string(),
|
|
39
|
+
line: line_num,
|
|
40
|
+
is_trailing,
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
comments
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/// Groups comments with declarations.
|
|
49
|
+
///
|
|
50
|
+
/// Associates each leading comment block with the next declaration's line.
|
|
51
|
+
/// Returns a map from declaration start line to its leading comments.
|
|
52
|
+
pub fn associate_comments_with_lines(
|
|
53
|
+
comments: &BTreeMap<usize, Vec<Comment>>,
|
|
54
|
+
declaration_lines: &[usize],
|
|
55
|
+
) -> BTreeMap<usize, Vec<Comment>> {
|
|
56
|
+
let mut associated: BTreeMap<usize, Vec<Comment>> = BTreeMap::new();
|
|
57
|
+
|
|
58
|
+
// Sort declaration lines
|
|
59
|
+
let mut decl_lines = declaration_lines.to_vec();
|
|
60
|
+
decl_lines.sort();
|
|
61
|
+
|
|
62
|
+
// For each declaration, find leading comments
|
|
63
|
+
for &decl_line in &decl_lines {
|
|
64
|
+
let mut leading_comments = Vec::new();
|
|
65
|
+
|
|
66
|
+
// Look backwards from the declaration for comment lines
|
|
67
|
+
let mut check_line = decl_line.saturating_sub(1);
|
|
68
|
+
while check_line > 0 {
|
|
69
|
+
if let Some(line_comments) = comments.get(&check_line) {
|
|
70
|
+
// Only consider non-trailing comments as leading
|
|
71
|
+
let non_trailing: Vec<_> = line_comments
|
|
72
|
+
.iter()
|
|
73
|
+
.filter(|c| !c.is_trailing)
|
|
74
|
+
.cloned()
|
|
75
|
+
.collect();
|
|
76
|
+
|
|
77
|
+
if non_trailing.is_empty() {
|
|
78
|
+
break;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Insert at beginning to maintain order
|
|
82
|
+
for c in non_trailing.into_iter().rev() {
|
|
83
|
+
leading_comments.insert(0, c);
|
|
84
|
+
}
|
|
85
|
+
check_line -= 1;
|
|
86
|
+
} else {
|
|
87
|
+
break;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if !leading_comments.is_empty() {
|
|
92
|
+
associated.insert(decl_line, leading_comments);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
associated
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/// Represents a source file with comments tracked separately.
|
|
100
|
+
#[derive(Debug, Clone)]
|
|
101
|
+
pub struct CommentedSource {
|
|
102
|
+
/// The original source code
|
|
103
|
+
pub source: String,
|
|
104
|
+
/// Map of line numbers to comments
|
|
105
|
+
pub comments: BTreeMap<usize, Vec<Comment>>,
|
|
106
|
+
/// Leading comments to output before the file header
|
|
107
|
+
pub file_header_comments: Vec<Comment>,
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
impl CommentedSource {
|
|
111
|
+
/// Parse source code and extract comments.
|
|
112
|
+
pub fn new(source: &str) -> Self {
|
|
113
|
+
let comments = extract_comments(source);
|
|
114
|
+
|
|
115
|
+
// Find the first non-comment, non-empty line for header comments
|
|
116
|
+
let mut file_header_comments = Vec::new();
|
|
117
|
+
for (line_idx, line) in source.lines().enumerate() {
|
|
118
|
+
let line_num = line_idx + 1;
|
|
119
|
+
let trimmed = line.trim();
|
|
120
|
+
|
|
121
|
+
if trimmed.is_empty() {
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if trimmed.starts_with("//") {
|
|
126
|
+
if let Some(line_comments) = comments.get(&line_num) {
|
|
127
|
+
file_header_comments.extend(line_comments.iter().cloned());
|
|
128
|
+
}
|
|
129
|
+
} else {
|
|
130
|
+
// Found first non-comment line, stop
|
|
131
|
+
break;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
Self {
|
|
136
|
+
source: source.to_string(),
|
|
137
|
+
comments,
|
|
138
|
+
file_header_comments,
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/// Get leading comments for a specific line.
|
|
143
|
+
pub fn leading_comments_for(&self, line: usize) -> Vec<&Comment> {
|
|
144
|
+
// Look at previous lines for consecutive comments
|
|
145
|
+
let mut result = Vec::new();
|
|
146
|
+
let mut check_line = line.saturating_sub(1);
|
|
147
|
+
|
|
148
|
+
while check_line > 0 {
|
|
149
|
+
if let Some(comments) = self.comments.get(&check_line) {
|
|
150
|
+
let non_trailing: Vec<_> = comments.iter().filter(|c| !c.is_trailing).collect();
|
|
151
|
+
|
|
152
|
+
if non_trailing.is_empty() {
|
|
153
|
+
break;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
for c in non_trailing.into_iter().rev() {
|
|
157
|
+
result.insert(0, c);
|
|
158
|
+
}
|
|
159
|
+
check_line -= 1;
|
|
160
|
+
} else {
|
|
161
|
+
break;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
result
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/// Check if there are any comments in the source.
|
|
169
|
+
pub fn has_comments(&self) -> bool {
|
|
170
|
+
!self.comments.is_empty()
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
#[cfg(test)]
|
|
175
|
+
mod tests {
|
|
176
|
+
use super::*;
|
|
177
|
+
|
|
178
|
+
#[test]
|
|
179
|
+
fn test_extract_line_comments() {
|
|
180
|
+
let source = r#"
|
|
181
|
+
// This is a comment
|
|
182
|
+
Entity "Foo"
|
|
183
|
+
"#;
|
|
184
|
+
let comments = extract_comments(source);
|
|
185
|
+
assert_eq!(comments.len(), 1);
|
|
186
|
+
let line2_comments = comments.get(&2).unwrap();
|
|
187
|
+
assert_eq!(line2_comments[0].text, "This is a comment");
|
|
188
|
+
assert!(!line2_comments[0].is_trailing);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
#[test]
|
|
192
|
+
fn test_extract_trailing_comments() {
|
|
193
|
+
let source = r#"Entity "Foo" // trailing comment
|
|
194
|
+
"#;
|
|
195
|
+
let comments = extract_comments(source);
|
|
196
|
+
let line1_comments = comments.get(&1).unwrap();
|
|
197
|
+
assert_eq!(line1_comments[0].text, "trailing comment");
|
|
198
|
+
assert!(line1_comments[0].is_trailing);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
#[test]
|
|
202
|
+
fn test_multiple_comments() {
|
|
203
|
+
let source = r#"
|
|
204
|
+
// Comment 1
|
|
205
|
+
// Comment 2
|
|
206
|
+
Entity "Foo"
|
|
207
|
+
"#;
|
|
208
|
+
let comments = extract_comments(source);
|
|
209
|
+
assert_eq!(comments.len(), 2);
|
|
210
|
+
assert!(comments.contains_key(&2));
|
|
211
|
+
assert!(comments.contains_key(&3));
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
#[test]
|
|
215
|
+
fn test_commented_source() {
|
|
216
|
+
let source = r#"// File header comment
|
|
217
|
+
Entity "Foo"
|
|
218
|
+
"#;
|
|
219
|
+
let cs = CommentedSource::new(source);
|
|
220
|
+
assert_eq!(cs.file_header_comments.len(), 1);
|
|
221
|
+
assert_eq!(cs.file_header_comments[0].text, "File header comment");
|
|
222
|
+
}
|
|
223
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
//! Formatter configuration options.
|
|
2
|
+
|
|
3
|
+
use serde::{Deserialize, Serialize};
|
|
4
|
+
|
|
5
|
+
/// Style of indentation to use.
|
|
6
|
+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
|
|
7
|
+
pub enum IndentStyle {
|
|
8
|
+
/// Use spaces for indentation (default).
|
|
9
|
+
#[default]
|
|
10
|
+
Spaces,
|
|
11
|
+
/// Use tabs for indentation.
|
|
12
|
+
Tabs,
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/// Configuration options for the SEA code formatter.
|
|
16
|
+
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
17
|
+
pub struct FormatConfig {
|
|
18
|
+
/// Style of indentation (spaces or tabs).
|
|
19
|
+
pub indent_style: IndentStyle,
|
|
20
|
+
/// Number of spaces per indentation level (ignored if using tabs).
|
|
21
|
+
pub indent_width: usize,
|
|
22
|
+
/// Maximum line width before wrapping (advisory).
|
|
23
|
+
pub max_line_width: usize,
|
|
24
|
+
/// Whether to ensure file ends with a newline.
|
|
25
|
+
pub trailing_newline: bool,
|
|
26
|
+
/// Whether to preserve comments in output.
|
|
27
|
+
pub preserve_comments: bool,
|
|
28
|
+
/// Whether to sort imports alphabetically.
|
|
29
|
+
pub sort_imports: bool,
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
impl Default for FormatConfig {
|
|
33
|
+
fn default() -> Self {
|
|
34
|
+
Self {
|
|
35
|
+
indent_style: IndentStyle::Spaces,
|
|
36
|
+
indent_width: 4,
|
|
37
|
+
max_line_width: 100,
|
|
38
|
+
trailing_newline: true,
|
|
39
|
+
preserve_comments: true,
|
|
40
|
+
sort_imports: true,
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
impl FormatConfig {
|
|
46
|
+
/// Create a new config with default values.
|
|
47
|
+
pub fn new() -> Self {
|
|
48
|
+
Self::default()
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/// Set the indent style.
|
|
52
|
+
pub fn with_indent_style(mut self, style: IndentStyle) -> Self {
|
|
53
|
+
self.indent_style = style;
|
|
54
|
+
self
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/// Set the indent width (for spaces).
|
|
58
|
+
pub fn with_indent_width(mut self, width: usize) -> Self {
|
|
59
|
+
self.indent_width = width;
|
|
60
|
+
self
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/// Set whether to use tabs.
|
|
64
|
+
pub fn with_tabs(mut self) -> Self {
|
|
65
|
+
self.indent_style = IndentStyle::Tabs;
|
|
66
|
+
self
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/// Get the string to use for one level of indentation.
|
|
70
|
+
pub fn indent_string(&self) -> String {
|
|
71
|
+
match self.indent_style {
|
|
72
|
+
IndentStyle::Spaces => " ".repeat(self.indent_width),
|
|
73
|
+
IndentStyle::Tabs => "\t".to_string(),
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
#[cfg(test)]
|
|
79
|
+
mod tests {
|
|
80
|
+
use super::*;
|
|
81
|
+
|
|
82
|
+
#[test]
|
|
83
|
+
fn test_default_config() {
|
|
84
|
+
let config = FormatConfig::default();
|
|
85
|
+
assert_eq!(config.indent_style, IndentStyle::Spaces);
|
|
86
|
+
assert_eq!(config.indent_width, 4);
|
|
87
|
+
assert_eq!(config.max_line_width, 100);
|
|
88
|
+
assert!(config.trailing_newline);
|
|
89
|
+
assert!(config.preserve_comments);
|
|
90
|
+
assert!(config.sort_imports);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
#[test]
|
|
94
|
+
fn test_indent_string_spaces() {
|
|
95
|
+
let config = FormatConfig::default().with_indent_width(2);
|
|
96
|
+
assert_eq!(config.indent_string(), " ");
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
#[test]
|
|
100
|
+
fn test_indent_string_tabs() {
|
|
101
|
+
let config = FormatConfig::default().with_tabs();
|
|
102
|
+
assert_eq!(config.indent_string(), "\t");
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
#[test]
|
|
106
|
+
fn test_builder_pattern() {
|
|
107
|
+
let config = FormatConfig::new()
|
|
108
|
+
.with_indent_style(IndentStyle::Tabs)
|
|
109
|
+
.with_indent_width(8);
|
|
110
|
+
|
|
111
|
+
assert_eq!(config.indent_style, IndentStyle::Tabs);
|
|
112
|
+
assert_eq!(config.indent_width, 8);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
//! SEA Code Formatter
|
|
2
|
+
//!
|
|
3
|
+
//! This module provides functionality to format SEA-DSL source code with
|
|
4
|
+
//! consistent whitespace, indentation, and styling.
|
|
5
|
+
//!
|
|
6
|
+
//! # Example
|
|
7
|
+
//!
|
|
8
|
+
//! ```rust,ignore
|
|
9
|
+
//! use domainforge_core::formatter::{format, FormatConfig};
|
|
10
|
+
//!
|
|
11
|
+
//! let source = r#"Entity "Foo" in bar"#;
|
|
12
|
+
//! let formatted = format(source, FormatConfig::default()).unwrap();
|
|
13
|
+
//! assert_eq!(formatted, "Entity \"Foo\" in bar\n");
|
|
14
|
+
//! ```
|
|
15
|
+
|
|
16
|
+
pub mod comments;
|
|
17
|
+
pub mod config;
|
|
18
|
+
pub mod printer;
|
|
19
|
+
|
|
20
|
+
pub use comments::CommentedSource;
|
|
21
|
+
pub use config::{FormatConfig, IndentStyle};
|
|
22
|
+
pub use printer::{format, format_preserving_comments, FormatError};
|