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.
Files changed (481) hide show
  1. package/.cargo/config.toml +6 -0
  2. package/.claude/settings.local.json +18 -0
  3. package/.coderabbit.yml +43 -0
  4. package/.codex/skills/release-management/SKILL.md +151 -0
  5. package/.codex/skills/release-management/agents/openai.yaml +4 -0
  6. package/.github/actions/decrypt-secrets/action.yml +121 -0
  7. package/.github/agents/Coder.agent.md +97 -0
  8. package/.github/agents/DeepResearch.agent.md +61 -0
  9. package/.github/chatmodes/tdd.vibepro.chatmode.md +1183 -0
  10. package/.github/copilot-instructions.md +13 -0
  11. package/.github/dependabot.yml +68 -0
  12. package/.github/workflows/README.md +165 -0
  13. package/.github/workflows/ci.yml +335 -0
  14. package/.github/workflows/dependabot-automerge.yml +114 -0
  15. package/.github/workflows/dependency-review.yml +27 -0
  16. package/.github/workflows/deploy.yml +87 -0
  17. package/.github/workflows/prepare-release.yml +168 -0
  18. package/.github/workflows/release-crates.yml +42 -0
  19. package/.github/workflows/release-npm.yml +137 -0
  20. package/.github/workflows/release-please.yml +29 -0
  21. package/.github/workflows/release-pypi.yml +96 -0
  22. package/.gitkeep +1 -0
  23. package/.release-please-manifest.json +5 -0
  24. package/.sea-registry.toml +10 -0
  25. package/.serena/project.yml +133 -0
  26. package/.sops.yaml +10 -0
  27. package/AGENTS.md +216 -0
  28. package/CHANGELOG.md +400 -0
  29. package/CLAUDE.md +62 -0
  30. package/CONTRIBUTING.md +323 -0
  31. package/Cargo.lock +3612 -0
  32. package/Cargo.toml +12 -0
  33. package/LICENSE +201 -0
  34. package/README.md +660 -0
  35. package/README_PYTHON.md +256 -0
  36. package/README_TYPESCRIPT.md +305 -0
  37. package/README_WASM.md +329 -0
  38. package/RELEASE_NOTES.md +41 -0
  39. package/bun.lock +378 -0
  40. package/bunfig.toml +11 -0
  41. package/check_output.txt +83 -0
  42. package/clippy_output.txt +80 -0
  43. package/commitlint.config.cjs +8 -0
  44. package/deny.toml +42 -0
  45. package/devbox.json +14 -0
  46. package/devbox.lock +76 -0
  47. package/docs/RELEASE_PROCESS.md +360 -0
  48. package/docs/diagnostics.md +161 -0
  49. package/docs/doc_guidelines.md +53 -0
  50. package/docs/explanations/README.md +21 -0
  51. package/docs/explanations/architecture-overview.md +109 -0
  52. package/docs/explanations/cross-language-binding-strategy.md +68 -0
  53. package/docs/explanations/graph-store-design.md +47 -0
  54. package/docs/explanations/performance-benchmarks.md +63 -0
  55. package/docs/explanations/policy-evaluation-logic.md +106 -0
  56. package/docs/explanations/semantic-modeling-concepts.md +109 -0
  57. package/docs/explanations/three-valued-logic.md +66 -0
  58. package/docs/explanations/versioning-strategy.md +45 -0
  59. package/docs/governance.md +168 -0
  60. package/docs/how-tos/README.md +46 -0
  61. package/docs/how-tos/ci-cd-validation.md +93 -0
  62. package/docs/how-tos/create-custom-units.md +125 -0
  63. package/docs/how-tos/define-policies.md +119 -0
  64. package/docs/how-tos/export-to-calm.md +110 -0
  65. package/docs/how-tos/export-to-protobuf.md +312 -0
  66. package/docs/how-tos/extend-grammar.md +133 -0
  67. package/docs/how-tos/generate-rdf-turtle.md +106 -0
  68. package/docs/how-tos/import-from-calm.md +114 -0
  69. package/docs/how-tos/import-from-sbvr.md +249 -0
  70. package/docs/how-tos/install-cli.md +126 -0
  71. package/docs/how-tos/parse-sea-files.md +132 -0
  72. package/docs/how-tos/policy-evaluation-modes.md +30 -0
  73. package/docs/how-tos/run-cross-language-tests.md +115 -0
  74. package/docs/how-tos/troubleshoot-napi-builds.md +55 -0
  75. package/docs/how-tos/use-modules-imports.md +285 -0
  76. package/docs/index.md +13 -0
  77. package/docs/plans/canonical-normalizer.md +121 -0
  78. package/docs/plans/cd_improvement.md +112 -0
  79. package/docs/plans/cli-ast.md +29 -0
  80. package/docs/plans/expression-bindings-and-normalizer-integration.md +174 -0
  81. package/docs/plans/protobuf_advanced_features_plan.md +597 -0
  82. package/docs/plans/protobuf_plan.yml +525 -0
  83. package/docs/plans/refactor_dsl_architecture.md +131 -0
  84. package/docs/plans/release-plan.md +163 -0
  85. package/docs/plans/sea_fmt_implementation_plan.md +516 -0
  86. package/docs/playbooks/README.md +18 -0
  87. package/docs/playbooks/adding-new-primitive.md +68 -0
  88. package/docs/playbooks/debugging-parser-failures.md +42 -0
  89. package/docs/playbooks/local-release-preparation.md +139 -0
  90. package/docs/playbooks/migrating-schema-versions.md +43 -0
  91. package/docs/playbooks/onboarding-contributors.md +64 -0
  92. package/docs/playbooks/releasing-beta.md +86 -0
  93. package/docs/playbooks/secret-management.md +64 -0
  94. package/docs/reference/README.md +199 -0
  95. package/docs/reference/ast-json-api.md +427 -0
  96. package/docs/reference/calm-mapping.md +519 -0
  97. package/docs/reference/cli-commands.md +588 -0
  98. package/docs/reference/configuration.md +202 -0
  99. package/docs/reference/error-codes.md +664 -0
  100. package/docs/reference/generated-artifacts-policy.md +53 -0
  101. package/docs/reference/grammar-spec.md +255 -0
  102. package/docs/reference/primitives-api.md +317 -0
  103. package/docs/reference/protobuf-api.md +426 -0
  104. package/docs/reference/python-api.md +485 -0
  105. package/docs/reference/registry.md +50 -0
  106. package/docs/reference/sea-dsl-ai-cheatsheet.yaml +913 -0
  107. package/docs/reference/security-model.md +74 -0
  108. package/docs/reference/typescript-api.md +508 -0
  109. package/docs/reference/wasm-api.md +420 -0
  110. package/docs/semantic-pack-review.md +144 -0
  111. package/docs/semantic-pack-signing.md +234 -0
  112. package/docs/semantic-packs.md +284 -0
  113. package/docs/specs/ADR-001-sea-dsl-semantic-source-of-truth.md +33 -0
  114. package/docs/specs/ADR-002-projection-first-class-construct.md +50 -0
  115. package/docs/specs/ADR-003-protobuf-projection-target.md +51 -0
  116. package/docs/specs/ADR-004-projection-compatibility-semantics.md +57 -0
  117. package/docs/specs/ADR-005-multi-language-support-strategy.md +112 -0
  118. package/docs/specs/ADR-006-error-handling-strategy.md +115 -0
  119. package/docs/specs/ADR-007-policy-evaluation-engine.md +95 -0
  120. package/docs/specs/ADR-008-knowledge-graph-integration.md +90 -0
  121. package/docs/specs/ADR-009-module-resolution-strategy.md +115 -0
  122. package/docs/specs/ADR-010-unit-system.md +106 -0
  123. package/docs/specs/PRD-001-sea-projection-framework.md +155 -0
  124. package/docs/specs/PRD-002-sea-cli-tooling.md +169 -0
  125. package/docs/specs/PRD-003-dsl-core-capabilities.md +275 -0
  126. package/docs/specs/README.md +62 -0
  127. package/docs/specs/SDS-001-protobuf-projection-engine.md +451 -0
  128. package/docs/specs/SDS-002-sea-core-architecture.md +268 -0
  129. package/docs/specs/SDS-003-parser-semantic-graph.md +377 -0
  130. package/docs/specs/SDS-004-policy-engine-design.md +362 -0
  131. package/docs/specs/SDS-005-knowledge-graph-module.md +364 -0
  132. package/docs/specs/SDS-006-calm-integration.md +367 -0
  133. package/docs/specs/SDS-007-sbvr-import.md +347 -0
  134. package/docs/templates/template_explanation.md +14 -0
  135. package/docs/templates/template_howto.md +21 -0
  136. package/docs/templates/template_playbook.md +21 -0
  137. package/docs/templates/template_reference.md +17 -0
  138. package/docs/templates/template_tutorial.md +24 -0
  139. package/docs/tutorials/README.md +12 -0
  140. package/docs/tutorials/first-sea-model.md +85 -0
  141. package/docs/tutorials/getting-started.md +98 -0
  142. package/docs/tutorials/python-binding-quickstart.md +107 -0
  143. package/docs/tutorials/typescript-binding-quickstart.md +91 -0
  144. package/docs/tutorials/wasm-in-browser.md +75 -0
  145. package/domainforge-core/CHANGELOG.md +138 -0
  146. package/domainforge-core/Cargo.toml +101 -0
  147. package/domainforge-core/MIGRATING.md +32 -0
  148. package/domainforge-core/README.md +197 -0
  149. package/domainforge-core/benchmark_results.txt +51 -0
  150. package/domainforge-core/build.rs +6 -0
  151. package/domainforge-core/deny.toml +31 -0
  152. package/domainforge-core/docs/specs/projections/sbvr_kg_mapping.md +43 -0
  153. package/domainforge-core/examples/basic.sea +7 -0
  154. package/domainforge-core/examples/cli/import_export_workflow.sh +38 -0
  155. package/domainforge-core/examples/cli/validate_example.sh +30 -0
  156. package/domainforge-core/examples/evolution_semantics.sea +31 -0
  157. package/domainforge-core/examples/parser_demo.rs +203 -0
  158. package/domainforge-core/grammar/sea.pest +408 -0
  159. package/domainforge-core/schemas/calm-v1.schema.json +170 -0
  160. package/domainforge-core/schemas/shacl/sea_shapes.ttl +19 -0
  161. package/domainforge-core/src/authority/compiler.rs +309 -0
  162. package/domainforge-core/src/authority/environment.rs +203 -0
  163. package/domainforge-core/src/authority/error.rs +164 -0
  164. package/domainforge-core/src/authority/fact_resolver.rs +224 -0
  165. package/domainforge-core/src/authority/mod.rs +25 -0
  166. package/domainforge-core/src/authority/pack.rs +133 -0
  167. package/domainforge-core/src/authority/policy.rs +224 -0
  168. package/domainforge-core/src/authority/resolver.rs +446 -0
  169. package/domainforge-core/src/authority/trace.rs +217 -0
  170. package/domainforge-core/src/authority/transform.rs +168 -0
  171. package/domainforge-core/src/authority/types.rs +617 -0
  172. package/domainforge-core/src/bin/domainforge.rs +25 -0
  173. package/domainforge-core/src/calm/export.rs +538 -0
  174. package/domainforge-core/src/calm/import.rs +1220 -0
  175. package/domainforge-core/src/calm/mod.rs +9 -0
  176. package/domainforge-core/src/calm/models.rs +108 -0
  177. package/domainforge-core/src/calm/sbvr_import.rs +9 -0
  178. package/domainforge-core/src/cli/authority.rs +149 -0
  179. package/domainforge-core/src/cli/format.rs +85 -0
  180. package/domainforge-core/src/cli/import.rs +133 -0
  181. package/domainforge-core/src/cli/mod.rs +64 -0
  182. package/domainforge-core/src/cli/normalize.rs +180 -0
  183. package/domainforge-core/src/cli/pack.rs +904 -0
  184. package/domainforge-core/src/cli/parse.rs +112 -0
  185. package/domainforge-core/src/cli/project.rs +294 -0
  186. package/domainforge-core/src/cli/registry.rs +41 -0
  187. package/domainforge-core/src/cli/test.rs +12 -0
  188. package/domainforge-core/src/cli/validate.rs +195 -0
  189. package/domainforge-core/src/cli/validate_kg.rs +80 -0
  190. package/domainforge-core/src/concept_id.rs +89 -0
  191. package/domainforge-core/src/error/diagnostics.rs +426 -0
  192. package/domainforge-core/src/error/fuzzy.rs +253 -0
  193. package/domainforge-core/src/error/mod.rs +13 -0
  194. package/domainforge-core/src/formatter/comments.rs +223 -0
  195. package/domainforge-core/src/formatter/config.rs +114 -0
  196. package/domainforge-core/src/formatter/mod.rs +22 -0
  197. package/domainforge-core/src/formatter/printer.rs +906 -0
  198. package/domainforge-core/src/graph/mod.rs +858 -0
  199. package/domainforge-core/src/graph/to_ast.rs +66 -0
  200. package/domainforge-core/src/kg.rs +1476 -0
  201. package/domainforge-core/src/kg_import.rs +251 -0
  202. package/domainforge-core/src/lib.rs +203 -0
  203. package/domainforge-core/src/module/mod.rs +1 -0
  204. package/domainforge-core/src/module/resolver.rs +260 -0
  205. package/domainforge-core/src/parser/ast.rs +2919 -0
  206. package/domainforge-core/src/parser/ast_convert.rs +494 -0
  207. package/domainforge-core/src/parser/ast_schema.rs +491 -0
  208. package/domainforge-core/src/parser/error.rs +291 -0
  209. package/domainforge-core/src/parser/lint.rs +39 -0
  210. package/domainforge-core/src/parser/mod.rs +193 -0
  211. package/domainforge-core/src/parser/printer.rs +702 -0
  212. package/domainforge-core/src/parser/profiles.rs +71 -0
  213. package/domainforge-core/src/parser/string_utils.rs +138 -0
  214. package/domainforge-core/src/patterns.rs +68 -0
  215. package/domainforge-core/src/policy/core.rs +1148 -0
  216. package/domainforge-core/src/policy/expression.rs +399 -0
  217. package/domainforge-core/src/policy/mod.rs +18 -0
  218. package/domainforge-core/src/policy/normalize.rs +1028 -0
  219. package/domainforge-core/src/policy/quantifier.rs +940 -0
  220. package/domainforge-core/src/policy/three_valued.rs +140 -0
  221. package/domainforge-core/src/policy/three_valued_microbench.rs +104 -0
  222. package/domainforge-core/src/policy/type_inference.rs +67 -0
  223. package/domainforge-core/src/policy/violation.rs +36 -0
  224. package/domainforge-core/src/primitives/concept_change.rs +61 -0
  225. package/domainforge-core/src/primitives/entity.rs +224 -0
  226. package/domainforge-core/src/primitives/flow.rs +111 -0
  227. package/domainforge-core/src/primitives/instance.rs +93 -0
  228. package/domainforge-core/src/primitives/mapping_contract.rs +50 -0
  229. package/domainforge-core/src/primitives/metric.rs +79 -0
  230. package/domainforge-core/src/primitives/mod.rs +25 -0
  231. package/domainforge-core/src/primitives/projection_contract.rs +50 -0
  232. package/domainforge-core/src/primitives/quantity.rs +56 -0
  233. package/domainforge-core/src/primitives/relation.rs +68 -0
  234. package/domainforge-core/src/primitives/resource.rs +237 -0
  235. package/domainforge-core/src/primitives/resource_instance.rs +88 -0
  236. package/domainforge-core/src/primitives/role.rs +49 -0
  237. package/domainforge-core/src/projection/buf.rs +404 -0
  238. package/domainforge-core/src/projection/contracts.rs +22 -0
  239. package/domainforge-core/src/projection/engine.rs +19 -0
  240. package/domainforge-core/src/projection/mod.rs +16 -0
  241. package/domainforge-core/src/projection/protobuf.rs +3331 -0
  242. package/domainforge-core/src/projection/registry.rs +43 -0
  243. package/domainforge-core/src/python/authority.rs +253 -0
  244. package/domainforge-core/src/python/error.rs +227 -0
  245. package/domainforge-core/src/python/formatter.rs +86 -0
  246. package/domainforge-core/src/python/graph.rs +366 -0
  247. package/domainforge-core/src/python/mod.rs +9 -0
  248. package/domainforge-core/src/python/policy.rs +651 -0
  249. package/domainforge-core/src/python/primitives.rs +796 -0
  250. package/domainforge-core/src/python/registry.rs +98 -0
  251. package/domainforge-core/src/python/semantic_pack.rs +619 -0
  252. package/domainforge-core/src/python/units.rs +96 -0
  253. package/domainforge-core/src/registry/mod.rs +432 -0
  254. package/domainforge-core/src/registry/tests.rs +210 -0
  255. package/domainforge-core/src/sbvr.rs +744 -0
  256. package/domainforge-core/src/semantic_pack/builder.rs +470 -0
  257. package/domainforge-core/src/semantic_pack/canonical_json.rs +184 -0
  258. package/domainforge-core/src/semantic_pack/diagnostics.rs +214 -0
  259. package/domainforge-core/src/semantic_pack/diff.rs +216 -0
  260. package/domainforge-core/src/semantic_pack/mod.rs +31 -0
  261. package/domainforge-core/src/semantic_pack/pack_set.rs +240 -0
  262. package/domainforge-core/src/semantic_pack/resolver.rs +437 -0
  263. package/domainforge-core/src/semantic_pack/review.rs +125 -0
  264. package/domainforge-core/src/semantic_pack/schema.rs +342 -0
  265. package/domainforge-core/src/semantic_pack/signing.rs +105 -0
  266. package/domainforge-core/src/semantic_pack/validator.rs +368 -0
  267. package/domainforge-core/src/semantic_version.rs +140 -0
  268. package/domainforge-core/src/test_utils.rs +12 -0
  269. package/domainforge-core/src/typescript/authority.rs +184 -0
  270. package/domainforge-core/src/typescript/error.rs +146 -0
  271. package/domainforge-core/src/typescript/formatter.rs +76 -0
  272. package/domainforge-core/src/typescript/graph.rs +391 -0
  273. package/domainforge-core/src/typescript/mod.rs +9 -0
  274. package/domainforge-core/src/typescript/policy.rs +564 -0
  275. package/domainforge-core/src/typescript/primitives.rs +784 -0
  276. package/domainforge-core/src/typescript/registry.rs +88 -0
  277. package/domainforge-core/src/typescript/semantic_pack.rs +470 -0
  278. package/domainforge-core/src/typescript/units.rs +76 -0
  279. package/domainforge-core/src/units/mod.rs +462 -0
  280. package/domainforge-core/src/uuid_module.rs +42 -0
  281. package/domainforge-core/src/validation_error.rs +818 -0
  282. package/domainforge-core/src/validation_result.rs +30 -0
  283. package/domainforge-core/src/wasm/authority.rs +192 -0
  284. package/domainforge-core/src/wasm/error.rs +145 -0
  285. package/domainforge-core/src/wasm/formatter.rs +69 -0
  286. package/domainforge-core/src/wasm/graph.rs +471 -0
  287. package/domainforge-core/src/wasm/mod.rs +16 -0
  288. package/domainforge-core/src/wasm/policy.rs +607 -0
  289. package/domainforge-core/src/wasm/primitives.rs +295 -0
  290. package/domainforge-core/src/wasm/semantic_pack.rs +471 -0
  291. package/domainforge-core/src/wasm/units.rs +62 -0
  292. package/domainforge-core/std/aws.sea +6 -0
  293. package/domainforge-core/std/core.sea +6 -0
  294. package/domainforge-core/std/http.sea +27 -0
  295. package/domainforge-core/tests/aggregation_enhanced_tests.rs +162 -0
  296. package/domainforge-core/tests/aggregation_eval_tests.rs +248 -0
  297. package/domainforge-core/tests/aggregation_integration_tests.rs +379 -0
  298. package/domainforge-core/tests/aggregation_parser_tests.rs +92 -0
  299. package/domainforge-core/tests/aggregation_tests.rs +102 -0
  300. package/domainforge-core/tests/authority_conformance_tests.rs +1173 -0
  301. package/domainforge-core/tests/calm_round_trip_tests.rs +283 -0
  302. package/domainforge-core/tests/calm_schema_validation_tests.rs +137 -0
  303. package/domainforge-core/tests/cast_operator_tests.rs +85 -0
  304. package/domainforge-core/tests/cli_binary_check.rs +37 -0
  305. package/domainforge-core/tests/cli_import_tests.rs +291 -0
  306. package/domainforge-core/tests/cli_path_traversal_tests.rs +124 -0
  307. package/domainforge-core/tests/cli_tests.rs +63 -0
  308. package/domainforge-core/tests/diagnostics_tests.rs +203 -0
  309. package/domainforge-core/tests/dimension_unit_tests.rs +80 -0
  310. package/domainforge-core/tests/entity_tests.rs +69 -0
  311. package/domainforge-core/tests/evolution_semantics_tests.rs +157 -0
  312. package/domainforge-core/tests/flow_tests.rs +78 -0
  313. package/domainforge-core/tests/flow_unit_validation_tests.rs +31 -0
  314. package/domainforge-core/tests/graph_integration_tests.rs +218 -0
  315. package/domainforge-core/tests/graph_tests.rs +626 -0
  316. package/domainforge-core/tests/import_parsing_tests.rs +23 -0
  317. package/domainforge-core/tests/instance_integration_tests.rs +98 -0
  318. package/domainforge-core/tests/instance_parsing_tests.rs +58 -0
  319. package/domainforge-core/tests/instance_tests.rs +61 -0
  320. package/domainforge-core/tests/kg_uri_encoding_tests.rs +53 -0
  321. package/domainforge-core/tests/lint_tests.rs +19 -0
  322. package/domainforge-core/tests/metric_tests.rs +143 -0
  323. package/domainforge-core/tests/module_resolution_tests.rs +100 -0
  324. package/domainforge-core/tests/namespace_registry_tests.rs +247 -0
  325. package/domainforge-core/tests/null_handling_tests.rs +26 -0
  326. package/domainforge-core/tests/parser_ast_v3.rs +53 -0
  327. package/domainforge-core/tests/parser_dimension_registry_tests.rs +20 -0
  328. package/domainforge-core/tests/parser_integration_tests.rs +294 -0
  329. package/domainforge-core/tests/parser_metadata_tests.rs +97 -0
  330. package/domainforge-core/tests/parser_resource_domain_only_graph_test.rs +21 -0
  331. package/domainforge-core/tests/parser_resource_limits_tests.rs +122 -0
  332. package/domainforge-core/tests/parser_tests.rs +512 -0
  333. package/domainforge-core/tests/pattern_semantics_tests.rs +87 -0
  334. package/domainforge-core/tests/phase_14_determinism_tests.rs +166 -0
  335. package/domainforge-core/tests/phase_15_validation_error_tests.rs +136 -0
  336. package/domainforge-core/tests/phase_16_unicode_tests.rs +248 -0
  337. package/domainforge-core/tests/phase_17_export_tests.rs +285 -0
  338. package/domainforge-core/tests/phase_17_round_trip_tests.rs +264 -0
  339. package/domainforge-core/tests/policy_tests.rs +635 -0
  340. package/domainforge-core/tests/primitives_integration_tests.rs +151 -0
  341. package/domainforge-core/tests/print_rdf_xml.rs +14 -0
  342. package/domainforge-core/tests/printer_tests.rs +204 -0
  343. package/domainforge-core/tests/profile_tests.rs +35 -0
  344. package/domainforge-core/tests/projection_contracts_tests.rs +154 -0
  345. package/domainforge-core/tests/protobuf_projection_tests.rs +199 -0
  346. package/domainforge-core/tests/quantity_tests.rs +41 -0
  347. package/domainforge-core/tests/rdf_xml_typed_literal_tests.rs +105 -0
  348. package/domainforge-core/tests/registry_schema_tests.rs +33 -0
  349. package/domainforge-core/tests/resource_tests.rs +50 -0
  350. package/domainforge-core/tests/resource_unit_tests.rs +24 -0
  351. package/domainforge-core/tests/roles_relations_tests.rs +61 -0
  352. package/domainforge-core/tests/round_trip_tests.rs +34 -0
  353. package/domainforge-core/tests/runtime_toggle_tests.rs +70 -0
  354. package/domainforge-core/tests/sbvr_fact_schema_tests.rs +60 -0
  355. package/domainforge-core/tests/sbvr_flow_facts_tests.rs +55 -0
  356. package/domainforge-core/tests/sbvr_parsing_tests.rs +53 -0
  357. package/domainforge-core/tests/semantic_pack_alias_resolution.rs +197 -0
  358. package/domainforge-core/tests/semantic_pack_build.rs +302 -0
  359. package/domainforge-core/tests/semantic_pack_consumer_smoke.rs +150 -0
  360. package/domainforge-core/tests/semantic_pack_pack_set.rs +160 -0
  361. package/domainforge-core/tests/semantic_pack_signing.rs +157 -0
  362. package/domainforge-core/tests/semantic_pack_three_valued.rs +250 -0
  363. package/domainforge-core/tests/semantic_pack_validate.rs +196 -0
  364. package/domainforge-core/tests/std_lib_tests.rs +37 -0
  365. package/domainforge-core/tests/temporal_evaluation_tests.rs +159 -0
  366. package/domainforge-core/tests/temporal_semantics_tests.rs +214 -0
  367. package/domainforge-core/tests/three_valued_quantifiers_tests.rs +164 -0
  368. package/domainforge-core/tests/turtle_entity_export_tests.rs +38 -0
  369. package/domainforge-core/tests/turtle_escaping_tests.rs +53 -0
  370. package/domainforge-core/tests/turtle_resource_export_tests.rs +34 -0
  371. package/domainforge-core/tests/type_inference_tests.rs +40 -0
  372. package/domainforge-core/tests/unicode_validation_tests.rs +169 -0
  373. package/domainforge-core/tests/unit_tests.rs +81 -0
  374. package/domainforge-core/tests/validate_tests.rs +38 -0
  375. package/domainforge-core/tests/validation_unit_mismatch_tests.rs +83 -0
  376. package/domainforge-core/tests/wasm_tests.rs +229 -0
  377. package/domainforge-python/CHANGELOG-python.md +12 -0
  378. package/domainforge-python/MIGRATING.md +24 -0
  379. package/domainforge-python/README.md +256 -0
  380. package/domainforge-python/domainforge/__init__.py +95 -0
  381. package/domainforge-python/domainforge/domainforge.pyi +519 -0
  382. package/domainforge-python/pyproject.toml +36 -0
  383. package/domainforge-typescript/CHANGELOG-typescript.md +12 -0
  384. package/domainforge-typescript/LICENSE +201 -0
  385. package/domainforge-typescript/MIGRATING.md +24 -0
  386. package/domainforge-typescript/README.md +305 -0
  387. package/domainforge-typescript/index.d.ts +452 -0
  388. package/domainforge-typescript/index.js +361 -0
  389. package/domainforge-typescript/package.json +60 -0
  390. package/example.js +61 -0
  391. package/examples/browser.html +366 -0
  392. package/examples/namespaces/finance/cashflow.sea +5 -0
  393. package/examples/namespaces/logistics/core.sea +7 -0
  394. package/examples/observability_metrics.sea +38 -0
  395. package/fixtures/semantic_packs/acme_procurement/domain/entities.sea +39 -0
  396. package/fixtures/semantic_packs/acme_procurement/domain/metrics.sea +11 -0
  397. package/fixtures/semantic_packs/acme_procurement/domain/relations.sea +7 -0
  398. package/fixtures/semantic_packs/acme_procurement/domain/resources.sea +9 -0
  399. package/fixtures/semantic_packs/acme_procurement/review/acme.procurement.semantic-review.jsonl +7 -0
  400. package/fixtures/semantic_packs/acme_procurement/tests/ambiguous_vendor_alias.sea +8 -0
  401. package/fixtures/semantic_packs/acme_procurement/tests/deprecated_vendor_alias.sea +8 -0
  402. package/fixtures/semantic_packs/acme_procurement/tests/invalid_relation.sea +3 -0
  403. package/fixtures/semantic_packs/acme_procurement/tests/proposed_concept.sea +8 -0
  404. package/fixtures/semantic_packs/acme_procurement/tests/rejected_concept.sea +8 -0
  405. package/fixtures/semantic_packs/acme_procurement/tests/unit_mismatch.sea +7 -0
  406. package/fixtures/semantic_packs/acme_procurement/tests/unknown_vendor_policy.sea +8 -0
  407. package/fixtures/semantic_packs/acme_procurement/tests/valid_purchase_policy.sea +8 -0
  408. package/index.d.ts +2 -0
  409. package/index.js +8 -0
  410. package/justfile +200 -0
  411. package/lefthook.yml +13 -0
  412. package/lib/validate_native_exports.d.ts +4 -0
  413. package/lib/validate_native_exports.js +12 -0
  414. package/package.json +22 -0
  415. package/pytest.ini +5 -0
  416. package/python/tests/test_registry.py +75 -0
  417. package/python/tests/test_units.py +18 -0
  418. package/release-please-config.json +49 -0
  419. package/requirements-dev.txt +3 -0
  420. package/requirements.txt +3 -0
  421. package/rust-toolchain.toml +3 -0
  422. package/schemas/ast-v1.schema.json +72 -0
  423. package/schemas/ast-v2.schema.json +1200 -0
  424. package/schemas/ast-v3.schema.json +1200 -0
  425. package/schemas/sea-registry.schema.json +45 -0
  426. package/scripts/build-python.sh +37 -0
  427. package/scripts/build-release.sh +279 -0
  428. package/scripts/build-typescript.sh +13 -0
  429. package/scripts/build-wasm.sh +113 -0
  430. package/scripts/bump-version.sh +245 -0
  431. package/scripts/check_unused_test_imports.py +85 -0
  432. package/scripts/ci_tasks.py +379 -0
  433. package/scripts/clear_debug_test.sh +10 -0
  434. package/scripts/create-github-release.sh +262 -0
  435. package/scripts/create-tag.sh +203 -0
  436. package/scripts/find_and_link_test_binary.sh +70 -0
  437. package/scripts/generate-changelog.sh +271 -0
  438. package/scripts/generate-release-notes.sh +205 -0
  439. package/scripts/lint_release_security.py +96 -0
  440. package/scripts/lint_release_workflows.py +82 -0
  441. package/scripts/lint_workflow_gates.py +113 -0
  442. package/scripts/optimized-wasm-build.sh +61 -0
  443. package/scripts/patch_napi_types.py +62 -0
  444. package/scripts/pre-release-check.sh +289 -0
  445. package/scripts/prepare_rust_debug.sh +52 -0
  446. package/scripts/release.sh +373 -0
  447. package/scripts/resolve_rust_binary.py +230 -0
  448. package/scripts/run_commitlint.sh +29 -0
  449. package/scripts/test-all.sh +77 -0
  450. package/scripts/update_launch_program.py +93 -0
  451. package/secrets/README.md +27 -0
  452. package/secrets/secrets.yaml +21 -0
  453. package/test_integration.py +67 -0
  454. package/tests/test_authority.py +328 -0
  455. package/tests/test_ci_tasks.py +143 -0
  456. package/tests/test_expression.py +256 -0
  457. package/tests/test_golden_payment_flow.py +42 -0
  458. package/tests/test_graph.py +127 -0
  459. package/tests/test_instance.py +136 -0
  460. package/tests/test_parser.py +82 -0
  461. package/tests/test_primitives.py +68 -0
  462. package/tests/test_role_relation_parity.py +56 -0
  463. package/tests/test_runtime_toggle.py +156 -0
  464. package/tests/test_semantic_pack.py +639 -0
  465. package/tests/test_three_valued_eval.py +159 -0
  466. package/tsconfig.json +30 -0
  467. package/typescript-tests/advanced.test.ts +165 -0
  468. package/typescript-tests/authority.test.ts +216 -0
  469. package/typescript-tests/expression.test.ts +228 -0
  470. package/typescript-tests/golden-payment-flow.test.ts +51 -0
  471. package/typescript-tests/graph.test.ts +142 -0
  472. package/typescript-tests/native-binding.test.ts +20 -0
  473. package/typescript-tests/primitives.test.ts +88 -0
  474. package/typescript-tests/registry.test.ts +122 -0
  475. package/typescript-tests/role_relation.test.ts +63 -0
  476. package/typescript-tests/runtime_toggle.test.ts +141 -0
  477. package/typescript-tests/semantic-pack.test.ts +556 -0
  478. package/typescript-tests/three_valued_eval.test.ts +135 -0
  479. package/typescript-tests/units.test.ts +36 -0
  480. package/vitest.config.ts +13 -0
  481. package/wasm_demo.html +225 -0
@@ -0,0 +1,245 @@
1
+ #!/usr/bin/env bash
2
+ # bump-version.sh - Semantic version bumping for DomainForge
3
+ # Usage: ./scripts/bump-version.sh [major|minor|patch|X.Y.Z] [OPTIONS]
4
+ #
5
+ # Updates version in:
6
+ # - domainforge-core/Cargo.toml
7
+ # - domainforge-python/pyproject.toml
8
+ # - domainforge-typescript/package.json
9
+
10
+ set -euo pipefail
11
+
12
+ # ============================================================================
13
+ # Configuration
14
+ # ============================================================================
15
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
16
+ PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
17
+
18
+ # Colors
19
+ RED='\033[0;31m'
20
+ GREEN='\033[0;32m'
21
+ YELLOW='\033[1;33m'
22
+ BLUE='\033[0;34m'
23
+ NC='\033[0m'
24
+
25
+ # Flags
26
+ DRY_RUN=false
27
+ NO_COMMIT=false
28
+
29
+ # ============================================================================
30
+ # Helper Functions
31
+ # ============================================================================
32
+ log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
33
+ log_success() { echo -e "${GREEN}[✓]${NC} $1"; }
34
+ log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
35
+ log_error() { echo -e "${RED}[✗]${NC} $1"; }
36
+
37
+ usage() {
38
+ cat <<EOF
39
+ Usage: $(basename "$0") [VERSION_TYPE] [OPTIONS]
40
+
41
+ Bump version across all DomainForge package files.
42
+
43
+ VERSION_TYPE:
44
+ major Bump major version (X.0.0)
45
+ minor Bump minor version (x.Y.0)
46
+ patch Bump patch version (x.y.Z)
47
+ X.Y.Z Set explicit version (e.g., 1.2.3)
48
+ X.Y.Z-suffix Set version with pre-release (e.g., 1.2.3-alpha)
49
+
50
+ OPTIONS:
51
+ --dry-run Show what would be changed without modifying files
52
+ --no-commit Update files but don't create git commit
53
+ -h, --help Show this help message
54
+
55
+ FILES UPDATED:
56
+ - domainforge-core/Cargo.toml
57
+ - domainforge-python/pyproject.toml
58
+ - domainforge-typescript/package.json
59
+
60
+ EXAMPLES:
61
+ $(basename "$0") patch # 0.6.2 -> 0.6.3
62
+ $(basename "$0") minor # 0.6.2 -> 0.7.0
63
+ $(basename "$0") major # 0.6.2 -> 1.0.0
64
+ $(basename "$0") 2.0.0 # Set explicit version
65
+ $(basename "$0") 1.0.0-beta.1 # Pre-release version
66
+ $(basename "$0") patch --dry-run # Preview changes
67
+ EOF
68
+ }
69
+
70
+ get_current_version() {
71
+ grep -m1 '^version = ' "$PROJECT_ROOT/domainforge-core/Cargo.toml" | sed 's/version = "\(.*\)"/\1/'
72
+ }
73
+
74
+ calculate_new_version() {
75
+ local current="$1"
76
+ local bump_type="$2"
77
+
78
+ # Handle explicit version
79
+ if [[ "$bump_type" =~ ^[0-9]+\.[0-9]+\.[0-9]+ ]]; then
80
+ echo "$bump_type"
81
+ return
82
+ fi
83
+
84
+ # Parse current version (strip pre-release suffix for calculation)
85
+ local base_version="${current%-*}"
86
+ IFS='.' read -r major minor patch <<< "$base_version"
87
+
88
+ case "$bump_type" in
89
+ major)
90
+ major=$((major + 1))
91
+ minor=0
92
+ patch=0
93
+ ;;
94
+ minor)
95
+ minor=$((minor + 1))
96
+ patch=0
97
+ ;;
98
+ patch)
99
+ patch=$((patch + 1))
100
+ ;;
101
+ *)
102
+ log_error "Invalid bump type: $bump_type"
103
+ exit 1
104
+ ;;
105
+ esac
106
+
107
+ echo "${major}.${minor}.${patch}"
108
+ }
109
+
110
+ update_cargo_toml() {
111
+ local current="$1"
112
+ local new="$2"
113
+
114
+ if $DRY_RUN; then
115
+ log_info "Would update domainforge-core/Cargo.toml: $current -> $new"
116
+ else
117
+ sed -i "s/^version = \"$current\"/version = \"$new\"/" "$PROJECT_ROOT/domainforge-core/Cargo.toml"
118
+ log_success "Updated domainforge-core/Cargo.toml"
119
+ fi
120
+ }
121
+
122
+ update_pyproject_toml() {
123
+ local current="$1"
124
+ local new="$2"
125
+
126
+ if $DRY_RUN; then
127
+ log_info "Would update pyproject.toml: $current -> $new"
128
+ else
129
+ sed -i "s/^version = \"$current\"/version = \"$new\"/" "$PROJECT_ROOT/domainforge-python/pyproject.toml"
130
+ log_success "Updated domainforge-python/pyproject.toml"
131
+ fi
132
+ }
133
+
134
+ update_package_json() {
135
+ local new="$1"
136
+
137
+ if $DRY_RUN; then
138
+ log_info "Would update package.json to: $new"
139
+ else
140
+ # Use jq to update version (avoids npm dependency issues)
141
+ cd "$PROJECT_ROOT"
142
+ jq --arg v "$new" '.version = $v' domainforge-typescript/package.json > domainforge-typescript/package.json.tmp && mv domainforge-typescript/package.json.tmp domainforge-typescript/package.json
143
+ log_success "Updated domainforge-typescript/package.json"
144
+ fi
145
+ }
146
+
147
+ # ============================================================================
148
+ # Parse Arguments
149
+ # ============================================================================
150
+ VERSION_TYPE=""
151
+
152
+ while [[ $# -gt 0 ]]; do
153
+ case $1 in
154
+ --dry-run)
155
+ DRY_RUN=true
156
+ shift
157
+ ;;
158
+ --no-commit)
159
+ NO_COMMIT=true
160
+ shift
161
+ ;;
162
+ -h|--help)
163
+ usage
164
+ exit 0
165
+ ;;
166
+ -*)
167
+ log_error "Unknown option: $1"
168
+ usage
169
+ exit 1
170
+ ;;
171
+ *)
172
+ if [[ -z "$VERSION_TYPE" ]]; then
173
+ VERSION_TYPE="$1"
174
+ else
175
+ log_error "Too many arguments"
176
+ usage
177
+ exit 1
178
+ fi
179
+ shift
180
+ ;;
181
+ esac
182
+ done
183
+
184
+ if [[ -z "$VERSION_TYPE" ]]; then
185
+ log_error "Version type required"
186
+ usage
187
+ exit 1
188
+ fi
189
+
190
+ # ============================================================================
191
+ # Main
192
+ # ============================================================================
193
+ cd "$PROJECT_ROOT"
194
+
195
+ echo ""
196
+ echo "=============================================="
197
+ echo " DomainForge Version Bump"
198
+ echo "=============================================="
199
+ echo ""
200
+
201
+ if $DRY_RUN; then
202
+ log_warn "DRY RUN MODE - No files will be modified"
203
+ echo ""
204
+ fi
205
+
206
+ # Get current version
207
+ CURRENT_VERSION=$(get_current_version)
208
+ log_info "Current version: $CURRENT_VERSION"
209
+
210
+ # Calculate new version
211
+ NEW_VERSION=$(calculate_new_version "$CURRENT_VERSION" "$VERSION_TYPE")
212
+ log_info "New version: $NEW_VERSION"
213
+ echo ""
214
+
215
+ # Validate new version
216
+ if [[ "$CURRENT_VERSION" == "$NEW_VERSION" ]]; then
217
+ log_warn "New version is same as current version"
218
+ fi
219
+
220
+ # Update files
221
+ log_info "Updating version files..."
222
+ update_cargo_toml "$CURRENT_VERSION" "$NEW_VERSION"
223
+ update_pyproject_toml "$CURRENT_VERSION" "$NEW_VERSION"
224
+ update_package_json "$NEW_VERSION"
225
+
226
+ # Create git commit
227
+ if ! $DRY_RUN && ! $NO_COMMIT; then
228
+ echo ""
229
+ log_info "Creating git commit..."
230
+ git add domainforge-core/Cargo.toml domainforge-python/pyproject.toml domainforge-typescript/package.json
231
+ git commit -m "chore: bump version to v$NEW_VERSION" --quiet
232
+ log_success "Created commit: chore: bump version to v$NEW_VERSION"
233
+ fi
234
+
235
+ echo ""
236
+ echo "=============================================="
237
+ log_success "Version bumped to $NEW_VERSION"
238
+ echo "=============================================="
239
+
240
+ # Output just the version for use in scripts
241
+ if $DRY_RUN; then
242
+ echo ""
243
+ echo "# Output (for script chaining):"
244
+ fi
245
+ echo "$NEW_VERSION"
@@ -0,0 +1,85 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Scan test files (domainforge-core/tests/*.rs) for `use` statements and detect likely unused imports.
4
+ Only flags imports where the `use` imports a single identifier and that identifier is not used elsewhere in the file.
5
+ This heuristic avoids removing multi-import groups that might be used via macros or shadowed names.
6
+ """
7
+
8
+ import re
9
+ from pathlib import Path
10
+
11
+ repo_root = Path(__file__).resolve().parents[1]
12
+
13
+
14
+ def get_test_files():
15
+ tests_dir = repo_root / "domainforge-core" / "tests"
16
+ return list(tests_dir.glob("*.rs"))
17
+
18
+
19
+ USE_RE = re.compile(r"^use\s+([^;]+);")
20
+ GROUPED_RE = re.compile(r"^(?P<prefix>[^:;{]+::)\{(?P<items>[^}]+)\}$")
21
+ SIMPLE_SINGLE_IDENT_RE = re.compile(r"^(?P<path>[^:]+)::(?P<ident>[A-Za-z0-9_]+)$")
22
+
23
+ candidates = []
24
+ for f in get_test_files():
25
+ text = f.read_text(encoding="utf8")
26
+ lines = text.splitlines()
27
+ for i, line in enumerate(lines, start=1):
28
+ m = USE_RE.match(line.strip())
29
+ if not m:
30
+ continue
31
+ use_expr = m.group(1).strip()
32
+ # Detect grouped imports like `foo::bar::{A, B as C}`
33
+ group_match = GROUPED_RE.match(use_expr)
34
+ if group_match:
35
+ prefix = group_match.group("prefix")
36
+ items = [it.strip() for it in group_match.group("items").split(",")]
37
+ for item in items:
38
+ # Handle `A as B` case, take alias as `B` or `A` when no alias
39
+ parts = [p.strip() for p in item.split(" as ")]
40
+ if len(parts) == 2:
41
+ ident = parts[1]
42
+ else:
43
+ ident = parts[0]
44
+ # If the item has a path `kg::KnowledgeGraph`, only the last segment
45
+ # (the type or ident) is relevant for references in the file.
46
+ if "::" in ident:
47
+ ident = ident.split("::")[-1]
48
+ # Don't try to remove common traits which are used via method calls
49
+ if ident == "FromStr":
50
+ continue
51
+ occ = [
52
+ j
53
+ for j, line_text in enumerate(lines, start=1)
54
+ if j != i and re.search(r"\b" + re.escape(ident) + r"\b", line_text)
55
+ ]
56
+ if not occ:
57
+ candidates.append((str(f), i, line.strip(), ident))
58
+ continue
59
+ # Try to capture single import names like `crate::graph::Graph` or `domainforge_core::Graph` or `std::fmt::Result`
60
+ # Remove trailing traits like "pub use"? Not present here
61
+ if "::" not in use_expr:
62
+ continue
63
+ # Extract the last path component
64
+ ident = use_expr.split("::")[-1]
65
+ if not ident.isidentifier():
66
+ continue
67
+ # Search if ident appears elsewhere in file
68
+ # Exclude the import line
69
+ occ = [
70
+ j
71
+ for j, line_text in enumerate(lines, start=1)
72
+ if j != i and re.search(r"\b" + re.escape(ident) + r"\b", line_text)
73
+ ]
74
+ # Skip flagging trait names which are used implicitly (FromStr)
75
+ if ident == "FromStr":
76
+ continue
77
+ if not occ:
78
+ candidates.append((str(f), i, line.strip(), ident))
79
+
80
+ if candidates:
81
+ print("Potential unused single-ident imports detected:")
82
+ for path, line_no, line, ident in candidates:
83
+ print(f"{path}:{line_no}: {line} (ident={ident})")
84
+ else:
85
+ print("No obvious single-identifier unused imports detected in test files.")
@@ -0,0 +1,379 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ CI Tasks Script - Cross-platform CI automation helpers.
4
+
5
+ This script consolidates common CI tasks to reduce duplication across
6
+ GitHub Actions workflows and ensure consistency between local and CI environments.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import argparse
12
+ import os
13
+ import platform
14
+ import shutil
15
+ import subprocess
16
+ import sys
17
+ import tarfile
18
+ import zipfile
19
+ from pathlib import Path
20
+ from typing import Optional
21
+
22
+
23
+ def check_size(file_path: str, max_bytes: int, label: str = "File") -> int:
24
+ """
25
+ Check if a file's size is within the specified limit.
26
+
27
+ Args:
28
+ file_path: Path to the file to check
29
+ max_bytes: Maximum allowed size in bytes
30
+ label: Human-readable label for error messages
31
+
32
+ Returns:
33
+ 0 if size is within limit, 1 otherwise
34
+ """
35
+ path = Path(file_path)
36
+
37
+ if not path.exists():
38
+ print(f"::error::File not found: {file_path}", file=sys.stderr)
39
+ return 1
40
+
41
+ if not path.is_file():
42
+ print(f"::error::Not a file: {file_path}", file=sys.stderr)
43
+ return 1
44
+
45
+ size = path.stat().st_size
46
+ size_mb = size / (1024 * 1024)
47
+ max_mb = max_bytes / (1024 * 1024)
48
+
49
+ print(f"{label} size: {size:,} bytes ({size_mb:.2f} MB)")
50
+
51
+ if size > max_bytes:
52
+ print(
53
+ f"::error::{label} exceeds {max_bytes:,} bytes ({max_mb:.2f} MB): "
54
+ f"actual size is {size:,} bytes ({size_mb:.2f} MB)",
55
+ file=sys.stderr,
56
+ )
57
+ return 1
58
+
59
+ print(f"[OK] {label} is within size limit ({max_mb:.2f} MB)")
60
+ return 0
61
+
62
+
63
+ def package_archive(
64
+ input_path: str,
65
+ output_path: str,
66
+ archive_format: Optional[str] = None,
67
+ base_name: Optional[str] = None,
68
+ ) -> int:
69
+ """
70
+ Package a file or directory into an archive.
71
+
72
+ Args:
73
+ input_path: Path to file or directory to package
74
+ output_path: Output archive path
75
+ archive_format: 'zip' or 'tar.gz' (auto-detected from output_path if not specified)
76
+ base_name: Base name for the archived file (defaults to input filename)
77
+
78
+ Returns:
79
+ 0 on success, 1 on failure
80
+ """
81
+ input_p = Path(input_path)
82
+ output_p = Path(output_path)
83
+
84
+ if not input_p.exists():
85
+ print(f"::error::Input path not found: {input_path}", file=sys.stderr)
86
+ return 1
87
+
88
+ # Auto-detect format from extension
89
+ if archive_format is None:
90
+ if output_path.endswith(".zip"):
91
+ archive_format = "zip"
92
+ elif output_path.endswith(".tar.gz") or output_path.endswith(".tgz"):
93
+ archive_format = "tar.gz"
94
+ else:
95
+ print(
96
+ f"::error::Cannot determine archive format from: {output_path}",
97
+ file=sys.stderr,
98
+ )
99
+ return 1
100
+
101
+ # Determine the name to use inside the archive
102
+ if base_name is None:
103
+ base_name = input_p.name
104
+
105
+ try:
106
+ output_p.parent.mkdir(parents=True, exist_ok=True)
107
+
108
+ if archive_format == "zip":
109
+ with zipfile.ZipFile(output_p, "w", zipfile.ZIP_DEFLATED) as zf:
110
+ if input_p.is_file():
111
+ zf.write(input_p, base_name)
112
+ else:
113
+ for file in input_p.rglob("*"):
114
+ if file.is_file():
115
+ arcname = base_name / file.relative_to(input_p)
116
+ zf.write(file, arcname)
117
+ print(f"[OK] Created ZIP archive: {output_p}")
118
+
119
+ elif archive_format == "tar.gz":
120
+ with tarfile.open(output_p, "w:gz") as tf:
121
+ if input_p.is_file():
122
+ tf.add(input_p, arcname=base_name)
123
+ else:
124
+ tf.add(input_p, arcname=base_name)
125
+ print(f"[OK] Created tar.gz archive: {output_p}")
126
+
127
+ else:
128
+ print(
129
+ f"::error::Unsupported archive format: {archive_format}",
130
+ file=sys.stderr,
131
+ )
132
+ return 1
133
+
134
+ return 0
135
+
136
+ except Exception as e:
137
+ print(f"::error::Failed to create archive: {e}", file=sys.stderr)
138
+ return 1
139
+
140
+
141
+ def verify_cli_binary(binary_path: str, expected_output: Optional[str] = None) -> int:
142
+ """
143
+ Verify that a CLI binary can execute successfully.
144
+
145
+ Args:
146
+ binary_path: Path to the binary to test
147
+ expected_output: Optional substring to check in output
148
+
149
+ Returns:
150
+ 0 if binary runs successfully, 1 otherwise
151
+ """
152
+ path = Path(binary_path)
153
+
154
+ if not path.exists():
155
+ print(f"::error::Binary not found: {binary_path}", file=sys.stderr)
156
+ return 1
157
+
158
+ if not path.is_file():
159
+ print(f"::error::Not a file: {binary_path}", file=sys.stderr)
160
+ return 1
161
+
162
+ # Make executable on Unix-like systems
163
+ if platform.system() != "Windows":
164
+ path.chmod(path.stat().st_mode | 0o111)
165
+
166
+ # Set up environment for dynamic libraries
167
+ env = os.environ.copy()
168
+ bin_dir = str(path.parent.absolute())
169
+
170
+ if platform.system() == "Darwin":
171
+ dyld_path = env.get("DYLD_LIBRARY_PATH", "")
172
+ env["DYLD_LIBRARY_PATH"] = f"{bin_dir}:{dyld_path}" if dyld_path else bin_dir
173
+ elif platform.system() == "Linux":
174
+ ld_path = env.get("LD_LIBRARY_PATH", "")
175
+ env["LD_LIBRARY_PATH"] = f"{bin_dir}:{ld_path}" if ld_path else bin_dir
176
+ elif platform.system() == "Windows":
177
+ path_env = env.get("PATH", "")
178
+ env["PATH"] = f"{bin_dir};{path_env}" if path_env else bin_dir
179
+
180
+ try:
181
+ print(f"Running: {binary_path} --version")
182
+ result = subprocess.run(
183
+ [str(path), "--version"],
184
+ capture_output=True,
185
+ text=True,
186
+ env=env,
187
+ timeout=10,
188
+ )
189
+
190
+ if result.returncode != 0:
191
+ print(
192
+ f"::error::Binary exited with code {result.returncode}", file=sys.stderr
193
+ )
194
+ print(f"stdout: {result.stdout}", file=sys.stderr)
195
+ print(f"stderr: {result.stderr}", file=sys.stderr)
196
+ return 1
197
+
198
+ output = result.stdout + result.stderr
199
+ print("[OK] Binary executed successfully")
200
+ print(f"Output: {output.strip()}")
201
+
202
+ if expected_output and expected_output not in output:
203
+ print(
204
+ f"::error::Expected output '{expected_output}' not found in: {output}",
205
+ file=sys.stderr,
206
+ )
207
+ return 1
208
+
209
+ return 0
210
+
211
+ except subprocess.TimeoutExpired:
212
+ print("::error::Binary execution timed out", file=sys.stderr)
213
+ return 1
214
+ except Exception as e:
215
+ print(f"::error::Failed to execute binary: {e}", file=sys.stderr)
216
+ return 1
217
+
218
+
219
+ def _is_safe_path(target_dir: str, member_path: str) -> bool:
220
+ abs_target = os.path.realpath(target_dir)
221
+ abs_member = os.path.realpath(os.path.join(target_dir, member_path))
222
+ return abs_member.startswith(abs_target + os.sep) or abs_member == abs_target
223
+
224
+
225
+ def _validate_tar_member(target_dir: str, member: tarfile.TarInfo) -> None:
226
+ if member.name.startswith("/") or ".." in member.name.split("/"):
227
+ raise ValueError(f"Unsafe path in tar: {member.name}")
228
+ if member.issym() or member.islnk():
229
+ raise ValueError(f"Symlink/hardlink in tar: {member.name}")
230
+ if not _is_safe_path(target_dir, member.name):
231
+ raise ValueError(f"Path traversal in tar: {member.name}")
232
+
233
+
234
+ def _validate_zip_member(target_dir: str, filename: str) -> None:
235
+ if filename.startswith("/") or ".." in filename.split("/"):
236
+ raise ValueError(f"Unsafe path in zip: {filename}")
237
+ if not _is_safe_path(target_dir, filename):
238
+ raise ValueError(f"Path traversal in zip: {filename}")
239
+
240
+
241
+ def unpack_and_verify(archive_path: str, binary_name: str) -> int:
242
+ """
243
+ Unpack an archive and verify the binary inside can execute.
244
+
245
+ Args:
246
+ archive_path: Path to the archive
247
+ binary_name: Name of the binary to verify
248
+
249
+ Returns:
250
+ 0 on success, 1 on failure
251
+ """
252
+ archive_p = Path(archive_path)
253
+
254
+ if not archive_p.exists():
255
+ print(f"::error::Archive not found: {archive_path}", file=sys.stderr)
256
+ return 1
257
+
258
+ temp_dir = Path("tmp_verify_unpack")
259
+ temp_dir.mkdir(exist_ok=True)
260
+
261
+ try:
262
+ if archive_path.endswith(".zip"):
263
+ with zipfile.ZipFile(archive_p, "r") as zf:
264
+ for info in zf.infolist():
265
+ _validate_zip_member(str(temp_dir), info.filename)
266
+ zf.extractall(temp_dir)
267
+ elif archive_path.endswith(".tar.gz") or archive_path.endswith(".tgz"):
268
+ with tarfile.open(archive_p, "r:gz") as tf:
269
+ for member in tf.getmembers():
270
+ _validate_tar_member(str(temp_dir), member)
271
+ if sys.version_info >= (3, 12):
272
+ tf.extractall(temp_dir, filter="data")
273
+ else:
274
+ tf.extractall(temp_dir)
275
+ else:
276
+ print(
277
+ f"::error::Unsupported archive format: {archive_path}", file=sys.stderr
278
+ )
279
+ return 1
280
+
281
+ print(f"[OK] Unpacked archive to {temp_dir}")
282
+
283
+ # Find the binary
284
+ binary_path = None
285
+ for file in temp_dir.rglob(binary_name):
286
+ if file.is_file():
287
+ binary_path = file
288
+ break
289
+
290
+ # Also check with .exe extension on Windows
291
+ if binary_path is None and platform.system() == "Windows":
292
+ for file in temp_dir.rglob(f"{binary_name}.exe"):
293
+ if file.is_file():
294
+ binary_path = file
295
+ break
296
+
297
+ if binary_path is None:
298
+ print(
299
+ f"::error::Binary '{binary_name}' not found in archive", file=sys.stderr
300
+ )
301
+ print("Archive contents:", file=sys.stderr)
302
+ for file in temp_dir.rglob("*"):
303
+ print(f" {file.relative_to(temp_dir)}", file=sys.stderr)
304
+ return 1
305
+
306
+ print(f"Found binary: {binary_path}")
307
+
308
+ # Verify it runs
309
+ return verify_cli_binary(str(binary_path))
310
+
311
+ finally:
312
+ # Cleanup
313
+ if temp_dir.exists():
314
+ shutil.rmtree(temp_dir, ignore_errors=True)
315
+
316
+
317
+ def main() -> int:
318
+ """Main entry point."""
319
+ parser = argparse.ArgumentParser(
320
+ description="CI tasks automation script",
321
+ formatter_class=argparse.RawDescriptionHelpFormatter,
322
+ )
323
+
324
+ subparsers = parser.add_subparsers(dest="command", help="Command to run")
325
+
326
+ # check-size command
327
+ size_parser = subparsers.add_parser("check-size", help="Check file size")
328
+ size_parser.add_argument("--file", required=True, help="File to check")
329
+ size_parser.add_argument(
330
+ "--max-bytes", type=int, required=True, help="Maximum size in bytes"
331
+ )
332
+ size_parser.add_argument(
333
+ "--label", default="File", help="Label for output messages"
334
+ )
335
+
336
+ # package command
337
+ package_parser = subparsers.add_parser("package", help="Package files into archive")
338
+ package_parser.add_argument(
339
+ "--input", required=True, help="Input file or directory"
340
+ )
341
+ package_parser.add_argument("--output", required=True, help="Output archive path")
342
+ package_parser.add_argument(
343
+ "--format", choices=["zip", "tar.gz"], help="Archive format"
344
+ )
345
+ package_parser.add_argument("--base-name", help="Base name for archived content")
346
+
347
+ # verify-cli command
348
+ verify_parser = subparsers.add_parser(
349
+ "verify-cli", help="Verify CLI binary executes"
350
+ )
351
+ verify_parser.add_argument("--binary", required=True, help="Path to binary")
352
+ verify_parser.add_argument("--expected-output", help="Expected substring in output")
353
+
354
+ # unpack-verify command
355
+ unpack_parser = subparsers.add_parser(
356
+ "unpack-verify", help="Unpack archive and verify binary"
357
+ )
358
+ unpack_parser.add_argument("--archive", required=True, help="Archive path")
359
+ unpack_parser.add_argument(
360
+ "--binary-name", required=True, help="Binary name to verify"
361
+ )
362
+
363
+ args = parser.parse_args()
364
+
365
+ if args.command == "check-size":
366
+ return check_size(args.file, args.max_bytes, args.label)
367
+ elif args.command == "package":
368
+ return package_archive(args.input, args.output, args.format, args.base_name)
369
+ elif args.command == "verify-cli":
370
+ return verify_cli_binary(args.binary, args.expected_output)
371
+ elif args.command == "unpack-verify":
372
+ return unpack_and_verify(args.archive, args.binary_name)
373
+ else:
374
+ parser.print_help()
375
+ return 1
376
+
377
+
378
+ if __name__ == "__main__":
379
+ sys.exit(main())