datahike-browser-tests 1.0.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 (324) hide show
  1. package/.circleci/config.yml +405 -0
  2. package/.circleci/scripts/gen_ci.clj +194 -0
  3. package/.cirrus.yml +60 -0
  4. package/.clj-kondo/babashka/sci/config.edn +1 -0
  5. package/.clj-kondo/babashka/sci/sci/core.clj +9 -0
  6. package/.clj-kondo/config.edn +95 -0
  7. package/.dir-locals.el +2 -0
  8. package/.github/FUNDING.yml +3 -0
  9. package/.github/ISSUE_TEMPLATE/1-bug-report.yml +68 -0
  10. package/.github/ISSUE_TEMPLATE/2-feature-request.yml +28 -0
  11. package/.github/ISSUE_TEMPLATE/config.yml +6 -0
  12. package/.github/pull_request_template.md +24 -0
  13. package/.github/workflows/native-image.yml +84 -0
  14. package/LICENSE +203 -0
  15. package/README.md +273 -0
  16. package/bb/deps.edn +9 -0
  17. package/bb/resources/github-fingerprints +3 -0
  18. package/bb/resources/native-image-tests/run-bb-pod-tests.clj +162 -0
  19. package/bb/resources/native-image-tests/run-libdatahike-tests +12 -0
  20. package/bb/resources/native-image-tests/run-native-image-tests +74 -0
  21. package/bb/resources/native-image-tests/run-python-tests +22 -0
  22. package/bb/resources/native-image-tests/testconfig.attr-refs.edn +6 -0
  23. package/bb/resources/native-image-tests/testconfig.edn +5 -0
  24. package/bb/resources/template/.settings/org.eclipse.jdt.apt.core.prefs +2 -0
  25. package/bb/resources/template/.settings/org.eclipse.jdt.core.prefs +9 -0
  26. package/bb/resources/template/.settings/org.eclipse.m2e.core.prefs +4 -0
  27. package/bb/resources/template/pom.xml +22 -0
  28. package/bb/src/tools/build.clj +132 -0
  29. package/bb/src/tools/clj_kondo.clj +32 -0
  30. package/bb/src/tools/deploy.clj +26 -0
  31. package/bb/src/tools/examples.clj +19 -0
  32. package/bb/src/tools/npm.clj +100 -0
  33. package/bb/src/tools/python.clj +14 -0
  34. package/bb/src/tools/release.clj +94 -0
  35. package/bb/src/tools/test.clj +148 -0
  36. package/bb/src/tools/version.clj +47 -0
  37. package/bb.edn +269 -0
  38. package/benchmark/src/benchmark/cli.clj +195 -0
  39. package/benchmark/src/benchmark/compare.clj +157 -0
  40. package/benchmark/src/benchmark/config.clj +316 -0
  41. package/benchmark/src/benchmark/measure.clj +187 -0
  42. package/benchmark/src/benchmark/store.clj +190 -0
  43. package/benchmark/test/benchmark/measure_test.clj +156 -0
  44. package/build.clj +30 -0
  45. package/config.edn +49 -0
  46. package/deps.edn +138 -0
  47. package/dev/sandbox.clj +82 -0
  48. package/dev/sandbox.cljs +127 -0
  49. package/dev/sandbox_benchmarks.clj +27 -0
  50. package/dev/sandbox_client.clj +87 -0
  51. package/dev/sandbox_transact_bench.clj +109 -0
  52. package/dev/user.clj +79 -0
  53. package/doc/README.md +96 -0
  54. package/doc/adl/README.md +6 -0
  55. package/doc/adl/adr-000-adr.org +28 -0
  56. package/doc/adl/adr-001-attribute-references.org +15 -0
  57. package/doc/adl/adr-002-build-tooling.org +54 -0
  58. package/doc/adl/adr-003-db-meta-data.md +52 -0
  59. package/doc/adl/adr-004-github-flow.md +40 -0
  60. package/doc/adl/adr-XYZ-template.md +30 -0
  61. package/doc/adl/index.org +3 -0
  62. package/doc/assets/datahike-logo.svg +3 -0
  63. package/doc/assets/datahiking-invoice.org +85 -0
  64. package/doc/assets/hhtree2.png +0 -0
  65. package/doc/assets/network_topology.svg +624 -0
  66. package/doc/assets/perf.png +0 -0
  67. package/doc/assets/schema_mindmap.mm +132 -0
  68. package/doc/assets/schema_mindmap.svg +970 -0
  69. package/doc/assets/temporal_index.mm +74 -0
  70. package/doc/backend-development.md +78 -0
  71. package/doc/bb-pod.md +89 -0
  72. package/doc/benchmarking.md +360 -0
  73. package/doc/bindings/edn-conversion.md +383 -0
  74. package/doc/cli.md +162 -0
  75. package/doc/cljdoc.edn +27 -0
  76. package/doc/cljs-support.md +133 -0
  77. package/doc/config.md +406 -0
  78. package/doc/contributing.md +114 -0
  79. package/doc/datalog-vs-sql.md +210 -0
  80. package/doc/datomic_differences.md +109 -0
  81. package/doc/development/pull-api-ns.md +186 -0
  82. package/doc/development/pull-frame-state-diagram.jpg +0 -0
  83. package/doc/distributed.md +566 -0
  84. package/doc/entity_spec.md +92 -0
  85. package/doc/gc.md +273 -0
  86. package/doc/java-api.md +808 -0
  87. package/doc/javascript-api.md +421 -0
  88. package/doc/libdatahike.md +86 -0
  89. package/doc/logging_and_error_handling.md +43 -0
  90. package/doc/norms.md +66 -0
  91. package/doc/schema-migration.md +85 -0
  92. package/doc/schema.md +287 -0
  93. package/doc/storage-backends.md +363 -0
  94. package/doc/store-id-refactoring.md +596 -0
  95. package/doc/time_variance.md +325 -0
  96. package/doc/unstructured.md +167 -0
  97. package/doc/versioning.md +261 -0
  98. package/examples/basic/README.md +19 -0
  99. package/examples/basic/deps.edn +6 -0
  100. package/examples/basic/docker-compose.yml +13 -0
  101. package/examples/basic/src/examples/core.clj +60 -0
  102. package/examples/basic/src/examples/schema.clj +155 -0
  103. package/examples/basic/src/examples/store.clj +60 -0
  104. package/examples/basic/src/examples/time_travel.clj +185 -0
  105. package/examples/java/.settings/org.eclipse.core.resources.prefs +3 -0
  106. package/examples/java/.settings/org.eclipse.jdt.apt.core.prefs +2 -0
  107. package/examples/java/.settings/org.eclipse.jdt.core.prefs +9 -0
  108. package/examples/java/.settings/org.eclipse.m2e.core.prefs +4 -0
  109. package/examples/java/README.md +162 -0
  110. package/examples/java/pom.xml +62 -0
  111. package/examples/java/src/main/java/examples/QuickStart.java +115 -0
  112. package/examples/java/src/main/java/examples/SchemaExample.java +148 -0
  113. package/examples/java/src/main/java/examples/TimeTravelExample.java +121 -0
  114. package/flake.lock +27 -0
  115. package/flake.nix +27 -0
  116. package/http-server/datahike/http/middleware.clj +75 -0
  117. package/http-server/datahike/http/server.clj +269 -0
  118. package/java/src/datahike/java/Database.java +274 -0
  119. package/java/src/datahike/java/Datahike.java +281 -0
  120. package/java/src/datahike/java/DatahikeGeneratedTest.java +349 -0
  121. package/java/src/datahike/java/DatahikeTest.java +370 -0
  122. package/java/src/datahike/java/EDN.java +170 -0
  123. package/java/src/datahike/java/IEntity.java +11 -0
  124. package/java/src/datahike/java/Keywords.java +161 -0
  125. package/java/src/datahike/java/SchemaFlexibility.java +52 -0
  126. package/java/src/datahike/java/Util.java +219 -0
  127. package/karma.conf.js +19 -0
  128. package/libdatahike/compile-cpp +7 -0
  129. package/libdatahike/src/datahike/impl/LibDatahikeBase.java +203 -0
  130. package/libdatahike/src/datahike/impl/libdatahike.clj +59 -0
  131. package/libdatahike/src/test_cpp.cpp +61 -0
  132. package/npm-package/PUBLISHING.md +140 -0
  133. package/npm-package/README.md +226 -0
  134. package/npm-package/package.template.json +34 -0
  135. package/npm-package/test-isomorphic.ts +281 -0
  136. package/npm-package/test.js +557 -0
  137. package/npm-package/typescript-test.ts +70 -0
  138. package/package.json +16 -0
  139. package/pydatahike/README.md +569 -0
  140. package/pydatahike/pyproject.toml +91 -0
  141. package/pydatahike/setup.py +42 -0
  142. package/pydatahike/src/datahike/__init__.py +134 -0
  143. package/pydatahike/src/datahike/_native.py +250 -0
  144. package/pydatahike/src/datahike/_version.py +2 -0
  145. package/pydatahike/src/datahike/database.py +722 -0
  146. package/pydatahike/src/datahike/edn.py +311 -0
  147. package/pydatahike/src/datahike/py.typed +0 -0
  148. package/pydatahike/tests/conftest.py +17 -0
  149. package/pydatahike/tests/test_basic.py +170 -0
  150. package/pydatahike/tests/test_database.py +51 -0
  151. package/pydatahike/tests/test_edn_conversion.py +299 -0
  152. package/pydatahike/tests/test_query.py +99 -0
  153. package/pydatahike/tests/test_schema.py +55 -0
  154. package/resources/clj-kondo.exports/io.replikativ/datahike/config.edn +5 -0
  155. package/resources/example_server.edn +4 -0
  156. package/shadow-cljs.edn +56 -0
  157. package/src/data_readers.clj +7 -0
  158. package/src/datahike/api/impl.cljc +176 -0
  159. package/src/datahike/api/specification.cljc +633 -0
  160. package/src/datahike/api/types.cljc +261 -0
  161. package/src/datahike/api.cljc +41 -0
  162. package/src/datahike/array.cljc +99 -0
  163. package/src/datahike/cli.clj +166 -0
  164. package/src/datahike/cljs.cljs +6 -0
  165. package/src/datahike/codegen/cli.clj +406 -0
  166. package/src/datahike/codegen/clj_kondo.clj +291 -0
  167. package/src/datahike/codegen/java.clj +403 -0
  168. package/src/datahike/codegen/naming.cljc +33 -0
  169. package/src/datahike/codegen/native.clj +559 -0
  170. package/src/datahike/codegen/pod.clj +488 -0
  171. package/src/datahike/codegen/python.clj +838 -0
  172. package/src/datahike/codegen/report.clj +55 -0
  173. package/src/datahike/codegen/typescript.clj +262 -0
  174. package/src/datahike/codegen/validation.clj +145 -0
  175. package/src/datahike/config.cljc +294 -0
  176. package/src/datahike/connections.cljc +16 -0
  177. package/src/datahike/connector.cljc +265 -0
  178. package/src/datahike/constants.cljc +142 -0
  179. package/src/datahike/core.cljc +297 -0
  180. package/src/datahike/datom.cljc +459 -0
  181. package/src/datahike/db/interface.cljc +119 -0
  182. package/src/datahike/db/search.cljc +305 -0
  183. package/src/datahike/db/transaction.cljc +937 -0
  184. package/src/datahike/db/utils.cljc +338 -0
  185. package/src/datahike/db.cljc +956 -0
  186. package/src/datahike/experimental/unstructured.cljc +126 -0
  187. package/src/datahike/experimental/versioning.cljc +172 -0
  188. package/src/datahike/externs.js +31 -0
  189. package/src/datahike/gc.cljc +69 -0
  190. package/src/datahike/http/client.clj +188 -0
  191. package/src/datahike/http/writer.clj +79 -0
  192. package/src/datahike/impl/entity.cljc +218 -0
  193. package/src/datahike/index/interface.cljc +93 -0
  194. package/src/datahike/index/persistent_set.cljc +469 -0
  195. package/src/datahike/index/utils.cljc +44 -0
  196. package/src/datahike/index.cljc +32 -0
  197. package/src/datahike/js/api.cljs +172 -0
  198. package/src/datahike/js/api_macros.clj +22 -0
  199. package/src/datahike/js.cljs +163 -0
  200. package/src/datahike/json.cljc +209 -0
  201. package/src/datahike/lru.cljc +146 -0
  202. package/src/datahike/migrate.clj +39 -0
  203. package/src/datahike/norm/norm.clj +245 -0
  204. package/src/datahike/online_gc.cljc +252 -0
  205. package/src/datahike/pod.clj +155 -0
  206. package/src/datahike/pull_api.cljc +325 -0
  207. package/src/datahike/query.cljc +1945 -0
  208. package/src/datahike/query_stats.cljc +88 -0
  209. package/src/datahike/readers.cljc +62 -0
  210. package/src/datahike/remote.cljc +218 -0
  211. package/src/datahike/schema.cljc +228 -0
  212. package/src/datahike/schema_cache.cljc +42 -0
  213. package/src/datahike/spec.cljc +101 -0
  214. package/src/datahike/store.cljc +80 -0
  215. package/src/datahike/tools.cljc +308 -0
  216. package/src/datahike/transit.cljc +80 -0
  217. package/src/datahike/writer.cljc +239 -0
  218. package/src/datahike/writing.cljc +362 -0
  219. package/src/deps.cljs +1 -0
  220. package/src-hitchhiker-tree/datahike/index/hitchhiker_tree/insert.cljc +76 -0
  221. package/src-hitchhiker-tree/datahike/index/hitchhiker_tree/upsert.cljc +128 -0
  222. package/src-hitchhiker-tree/datahike/index/hitchhiker_tree.cljc +213 -0
  223. package/test/datahike/backward_compatibility_test/src/backward_test.clj +37 -0
  224. package/test/datahike/integration_test/config_record_file_test.clj +14 -0
  225. package/test/datahike/integration_test/config_record_test.clj +14 -0
  226. package/test/datahike/integration_test/depr_config_uri_test.clj +15 -0
  227. package/test/datahike/integration_test/return_map_test.clj +62 -0
  228. package/test/datahike/integration_test.cljc +67 -0
  229. package/test/datahike/norm/norm_test.clj +124 -0
  230. package/test/datahike/norm/resources/naming-and-sorting-test/001-a1-example.edn +5 -0
  231. package/test/datahike/norm/resources/naming-and-sorting-test/002-a2-example.edn +5 -0
  232. package/test/datahike/norm/resources/naming-and-sorting-test/003-tx-fn-test.edn +1 -0
  233. package/test/datahike/norm/resources/naming-and-sorting-test/004-tx-data-and-tx-fn-test.edn +5 -0
  234. package/test/datahike/norm/resources/naming-and-sorting-test/01-transact-basic-characters.edn +2 -0
  235. package/test/datahike/norm/resources/naming-and-sorting-test/02 add occupation.edn +5 -0
  236. package/test/datahike/norm/resources/naming-and-sorting-test/checksums.edn +12 -0
  237. package/test/datahike/norm/resources/simple-test/001-a1-example.edn +5 -0
  238. package/test/datahike/norm/resources/simple-test/002-a2-example.edn +5 -0
  239. package/test/datahike/norm/resources/simple-test/checksums.edn +4 -0
  240. package/test/datahike/norm/resources/tx-data-and-tx-fn-test/first/001-a1-example.edn +5 -0
  241. package/test/datahike/norm/resources/tx-data-and-tx-fn-test/first/002-a2-example.edn +5 -0
  242. package/test/datahike/norm/resources/tx-data-and-tx-fn-test/first/003-tx-fn-test.edn +1 -0
  243. package/test/datahike/norm/resources/tx-data-and-tx-fn-test/first/checksums.edn +6 -0
  244. package/test/datahike/norm/resources/tx-data-and-tx-fn-test/second/004-tx-data-and-tx-fn-test.edn +5 -0
  245. package/test/datahike/norm/resources/tx-data-and-tx-fn-test/second/checksums.edn +2 -0
  246. package/test/datahike/norm/resources/tx-fn-test/first/001-a1-example.edn +5 -0
  247. package/test/datahike/norm/resources/tx-fn-test/first/002-a2-example.edn +5 -0
  248. package/test/datahike/norm/resources/tx-fn-test/first/checksums.edn +4 -0
  249. package/test/datahike/norm/resources/tx-fn-test/second/003-tx-fn-test.edn +1 -0
  250. package/test/datahike/norm/resources/tx-fn-test/second/checksums.edn +2 -0
  251. package/test/datahike/test/api_test.cljc +895 -0
  252. package/test/datahike/test/array_test.cljc +40 -0
  253. package/test/datahike/test/attribute_refs/datoms_test.cljc +140 -0
  254. package/test/datahike/test/attribute_refs/db_test.cljc +42 -0
  255. package/test/datahike/test/attribute_refs/differences_test.cljc +515 -0
  256. package/test/datahike/test/attribute_refs/entity_test.cljc +89 -0
  257. package/test/datahike/test/attribute_refs/pull_api_test.cljc +320 -0
  258. package/test/datahike/test/attribute_refs/query_find_specs_test.cljc +59 -0
  259. package/test/datahike/test/attribute_refs/query_fns_test.cljc +130 -0
  260. package/test/datahike/test/attribute_refs/query_interop_test.cljc +47 -0
  261. package/test/datahike/test/attribute_refs/query_not_test.cljc +193 -0
  262. package/test/datahike/test/attribute_refs/query_or_test.cljc +137 -0
  263. package/test/datahike/test/attribute_refs/query_pull_test.cljc +156 -0
  264. package/test/datahike/test/attribute_refs/query_rules_test.cljc +176 -0
  265. package/test/datahike/test/attribute_refs/query_test.cljc +241 -0
  266. package/test/datahike/test/attribute_refs/temporal_search.cljc +22 -0
  267. package/test/datahike/test/attribute_refs/transact_test.cljc +220 -0
  268. package/test/datahike/test/attribute_refs/utils.cljc +128 -0
  269. package/test/datahike/test/cache_test.cljc +38 -0
  270. package/test/datahike/test/components_test.cljc +92 -0
  271. package/test/datahike/test/config_test.cljc +158 -0
  272. package/test/datahike/test/core_test.cljc +105 -0
  273. package/test/datahike/test/datom_test.cljc +44 -0
  274. package/test/datahike/test/db_test.cljc +54 -0
  275. package/test/datahike/test/entity_spec_test.cljc +159 -0
  276. package/test/datahike/test/entity_test.cljc +103 -0
  277. package/test/datahike/test/explode_test.cljc +143 -0
  278. package/test/datahike/test/filter_test.cljc +75 -0
  279. package/test/datahike/test/gc_test.cljc +159 -0
  280. package/test/datahike/test/http/server_test.clj +192 -0
  281. package/test/datahike/test/http/writer_test.clj +86 -0
  282. package/test/datahike/test/ident_test.cljc +32 -0
  283. package/test/datahike/test/index_test.cljc +345 -0
  284. package/test/datahike/test/insert.cljc +125 -0
  285. package/test/datahike/test/java_bindings_test.clj +6 -0
  286. package/test/datahike/test/listen_test.cljc +41 -0
  287. package/test/datahike/test/lookup_refs_test.cljc +266 -0
  288. package/test/datahike/test/lru_test.cljc +27 -0
  289. package/test/datahike/test/migrate_test.clj +297 -0
  290. package/test/datahike/test/model/core.cljc +376 -0
  291. package/test/datahike/test/model/invariant.cljc +142 -0
  292. package/test/datahike/test/model/rng.cljc +82 -0
  293. package/test/datahike/test/model_test.clj +217 -0
  294. package/test/datahike/test/nodejs_test.cljs +262 -0
  295. package/test/datahike/test/online_gc_test.cljc +475 -0
  296. package/test/datahike/test/pod_test.clj +369 -0
  297. package/test/datahike/test/pull_api_test.cljc +474 -0
  298. package/test/datahike/test/purge_test.cljc +144 -0
  299. package/test/datahike/test/query_aggregates_test.cljc +101 -0
  300. package/test/datahike/test/query_find_specs_test.cljc +52 -0
  301. package/test/datahike/test/query_fns_test.cljc +523 -0
  302. package/test/datahike/test/query_interop_test.cljc +47 -0
  303. package/test/datahike/test/query_not_test.cljc +189 -0
  304. package/test/datahike/test/query_or_test.cljc +158 -0
  305. package/test/datahike/test/query_pull_test.cljc +147 -0
  306. package/test/datahike/test/query_rules_test.cljc +248 -0
  307. package/test/datahike/test/query_stats_test.cljc +218 -0
  308. package/test/datahike/test/query_test.cljc +984 -0
  309. package/test/datahike/test/schema_test.cljc +424 -0
  310. package/test/datahike/test/specification_test.cljc +30 -0
  311. package/test/datahike/test/store_test.cljc +78 -0
  312. package/test/datahike/test/stress_test.cljc +57 -0
  313. package/test/datahike/test/time_variance_test.cljc +518 -0
  314. package/test/datahike/test/tools_test.clj +134 -0
  315. package/test/datahike/test/transact_test.cljc +518 -0
  316. package/test/datahike/test/tuples_test.cljc +564 -0
  317. package/test/datahike/test/unstructured_test.cljc +291 -0
  318. package/test/datahike/test/upsert_impl_test.cljc +205 -0
  319. package/test/datahike/test/upsert_test.cljc +363 -0
  320. package/test/datahike/test/utils.cljc +110 -0
  321. package/test/datahike/test/validation_test.cljc +48 -0
  322. package/test/datahike/test/versioning_test.cljc +56 -0
  323. package/test/datahike/test.cljc +66 -0
  324. package/tests.edn +24 -0
@@ -0,0 +1,406 @@
1
+ (ns datahike.codegen.cli
2
+ "Generate CLI commands from API specification.
3
+
4
+ This namespace provides CLI-specific configuration and logic for:
5
+ - Deriving command names from operation categories
6
+ - Validating arguments with malli
7
+ - Generating hierarchical help text
8
+ - Handling stdin and file inputs"
9
+ (:require [clojure.string :as str]
10
+ [clojure.java.io :as io]
11
+ [clojure.edn :as edn]
12
+ [datahike.api.specification :refer [api-specification malli-schema->argslist]]
13
+ [datahike.api.types :as types]
14
+ [datahike.api :as d]
15
+ [datahike.json :as json]
16
+ [jsonista.core :as j]
17
+ [clj-cbor.core :as cbor]
18
+ [malli.core :as m]
19
+ [malli.error :as me])
20
+ (:import [java.util Date]))
21
+
22
+ ;; =============================================================================
23
+ ;; CLI-Specific Configuration
24
+ ;; =============================================================================
25
+
26
+ (def cli-config
27
+ "CLI-specific configuration that extends the universal API specification.
28
+
29
+ Keys:
30
+ - :stdin? - Whether this operation accepts stdin input
31
+ - :stdin-arg - Which argument position (0-indexed) accepts stdin
32
+ - :file-arg - Which argument position supports --file option
33
+ - :batch-size - For streaming operations, how many items per batch"
34
+
35
+ {'transact {:stdin? true
36
+ :stdin-arg 1 ; tx-data argument
37
+ :file-arg 1
38
+ :batch-size 1000}
39
+
40
+ 'load-entities {:stdin? true
41
+ :stdin-arg 1 ; entities argument
42
+ :batch-size 5000}})
43
+
44
+ (def cli-excluded-operations
45
+ "Operations excluded from CLI because they don't make sense in single-shot execution.
46
+
47
+ Reasons for exclusion:
48
+ - listen/unlisten: Require persistent connection with callbacks, CLI exits immediately
49
+ - release: Connection automatically released on CLI exit
50
+ - db (function): Redundant with db: prefix syntax
51
+ - tempid: Only useful within transactions, not standalone
52
+ - entity-db: Returns DB an entity came from, limited utility in CLI
53
+ - as-of/since/history/filter: Return DB objects that can't be serialized/deserialized,
54
+ use prefix syntax instead (e.g., asof:timestamp:config.edn)
55
+ - connect: Redundant with conn: prefix syntax
56
+ - db-with: Returns db value that can't be used in subsequent commands
57
+ - is-filtered: Requires filtered db input, which can't exist in CLI (filter excluded)"
58
+ #{'listen 'unlisten 'release 'db 'tempid 'entity-db
59
+ 'as-of 'since 'history 'filter
60
+ 'connect 'db-with 'is-filtered})
61
+
62
+ (defn cli-spec
63
+ "Get merged specification with CLI-specific config."
64
+ [op-name]
65
+ (merge (get api-specification op-name)
66
+ (get cli-config op-name)))
67
+
68
+ ;; =============================================================================
69
+ ;; Command Name Derivation
70
+ ;; =============================================================================
71
+
72
+ (defn ->cli-command
73
+ "Derive CLI command from operation name - flat structure.
74
+ Returns a single-element vector with the command name.
75
+
76
+ Examples:
77
+ 'database-exists? => [\"database-exists\"]
78
+ 'transact => [\"transact\"]
79
+ 'q => [\"query\"]
80
+ 'pull => [\"pull\"]"
81
+ [op-name categories]
82
+ (let [op-str (-> (name op-name)
83
+ (str/replace #"[?!]$" ""))] ; Remove ? and !
84
+ ;; Special case: expand 'q to "query" for clarity
85
+ [(if (= op-str "q") "query" op-str)]))
86
+
87
+ (defn command->string
88
+ "Convert command vector to string: [\"query\"] => \"query\""
89
+ [cmd-vec]
90
+ (str/join " " cmd-vec))
91
+
92
+ (defn build-command-index
93
+ "Build a lookup map from command string to operation name.
94
+
95
+ Returns: {\"database-exists\" 'database-exists?, \"transact\" 'transact, ...}"
96
+ []
97
+ (into {}
98
+ (for [[op-name {:keys [categories]}] api-specification
99
+ :when (not (contains? cli-excluded-operations op-name))
100
+ :let [cmd (->cli-command op-name categories)]]
101
+ [(command->string cmd) op-name])))
102
+
103
+ ;; =============================================================================
104
+ ;; Schema to Help Text
105
+ ;; =============================================================================
106
+
107
+ (def type-descriptions
108
+ "Human-readable descriptions for common malli types."
109
+ {:datahike/SConfig "database configuration (map or file path)"
110
+ :datahike/SConnection "database connection"
111
+ :datahike/SDB "database value"
112
+ :datahike/SEId "entity identifier (number, keyword, or lookup ref)"
113
+ :datahike/STransactions "transaction data (vector of maps)"
114
+ :datahike/STransactionReport "transaction report map"
115
+ :datahike/SQueryArgs "query arguments map"
116
+ :datahike/SPullOptions "pull pattern options"
117
+ :datahike/SDatoms "sequence of datoms"
118
+ :datahike/SSchema "database schema map"
119
+ :datahike/SMetrics "database metrics map"
120
+ :string "string"
121
+ :int "integer"
122
+ :boolean "boolean"
123
+ :keyword "keyword"
124
+ :map "map"
125
+ :vector "vector"
126
+ :any "any value"})
127
+
128
+ (defn schema->help-text
129
+ "Convert a malli schema to human-readable help text.
130
+
131
+ Examples:
132
+ :datahike/SConfig => \"config\"
133
+ [:tuple :datahike/SConfig] => \"config\"
134
+ [:tuple :datahike/SConnection :datahike/STransactions] => \"connection, transactions\""
135
+ [schema]
136
+ (cond
137
+ ;; Keyword reference - look up description
138
+ (keyword? schema)
139
+ (or (type-descriptions schema)
140
+ (name schema))
141
+
142
+ ;; Tuple - list the args
143
+ (and (vector? schema) (= :tuple (first schema)))
144
+ (str/join ", " (map #(or (type-descriptions %) (str %)) (rest schema)))
145
+
146
+ ;; :or - show alternatives
147
+ (and (vector? schema) (= :or (first schema)))
148
+ (str/join " or " (map schema->help-text (rest schema)))
149
+
150
+ ;; :=> function schema - extract input
151
+ (and (vector? schema) (= :=> (first schema)))
152
+ (let [[_ input-schema _] schema]
153
+ (schema->help-text input-schema))
154
+
155
+ ;; :cat - sequential args
156
+ (and (vector? schema) (= :cat (first schema)))
157
+ (str/join ", " (map schema->help-text (rest schema)))
158
+
159
+ ;; :function multi-arity - show first arity
160
+ (and (vector? schema) (= :function (first schema)))
161
+ (schema->help-text (second schema))
162
+
163
+ ;; [:sequential :any] or similar
164
+ (and (vector? schema) (= :sequential (first schema)))
165
+ "sequence"
166
+
167
+ ;; Default
168
+ :else (str schema)))
169
+
170
+ (defn args-help
171
+ "Generate argument help text for an operation."
172
+ [args-schema]
173
+ (schema->help-text args-schema))
174
+
175
+ ;; =============================================================================
176
+ ;; Help Text Generation
177
+ ;; =============================================================================
178
+
179
+ (defn first-sentence
180
+ "Extract first sentence from docstring."
181
+ [doc]
182
+ (-> doc
183
+ (str/split #"\.\s")
184
+ first
185
+ (str ".")))
186
+
187
+ (defn group-by-category
188
+ "Group operations by their primary category.
189
+ Returns: {category-keyword [op-names...]}"
190
+ []
191
+ (reduce
192
+ (fn [acc [op-name {:keys [categories]}]]
193
+ (if (contains? cli-excluded-operations op-name)
194
+ acc
195
+ (let [primary-cat (first categories)]
196
+ (update acc primary-cat (fnil conj []) op-name))))
197
+ {}
198
+ api-specification))
199
+
200
+ (def category-titles
201
+ "Human-readable titles for categories."
202
+ {:database "Database Operations"
203
+ :connection "Connection Operations"
204
+ :transaction "Transaction Operations"
205
+ :query "Query Operations"
206
+ :pull "Pull Operations"
207
+ :index "Index Operations"
208
+ :lifecycle "Lifecycle Operations"
209
+ :schema "Schema Operations"
210
+ :advanced "Advanced Operations"
211
+ :write "Write Operations"
212
+ :read "Read Operations"
213
+ :diagnostics "Diagnostics"
214
+ :maintenance "Maintenance"})
215
+
216
+ (def category-order
217
+ "Order for displaying categories in help."
218
+ [:database :connection :transaction :query :pull :schema :diagnostics :maintenance])
219
+
220
+ (defn generate-help
221
+ "Generate hierarchical help text from API specification."
222
+ [options-summary]
223
+ (let [grouped (group-by-category)
224
+ banner "▁▃▅▄▇▅▃▅▃▁ \033[1mdata\033[0mhike cli"
225
+ version (or (try (some-> (clojure.java.io/resource "DATAHIKE_VERSION")
226
+ slurp
227
+ str/trim)
228
+ (catch Exception _ nil))
229
+ "development")]
230
+ (str/join
231
+ "\n"
232
+ (concat
233
+ [banner
234
+ (str "version " version)
235
+ ""
236
+ "This is the Datahike command line interface."
237
+ ""
238
+ "The commands reflect the datahike.api Clojure API."
239
+ "Use db:config_file for database values, conn:config_file for connections."
240
+ ""
241
+ "Usage: dthk [options] <command> [arguments]"
242
+ ""
243
+ "Options:"
244
+ options-summary
245
+ ""
246
+ "Commands:"
247
+ ""]
248
+
249
+ ;; Generate hierarchical command listing
250
+ (mapcat
251
+ (fn [category]
252
+ (concat
253
+ [(str " " (get category-titles category category) ":")]
254
+ (for [op-name (get grouped category)
255
+ :let [{:keys [args doc]} (cli-spec op-name)
256
+ cmd (->cli-command op-name (list category))
257
+ cmd-str (command->string cmd)
258
+ args-str (args-help args)]]
259
+ (format " %-25s %s"
260
+ (str cmd-str " " args-str)
261
+ (first-sentence doc)))
262
+ [""]))
263
+ (filter #(contains? grouped %) category-order))
264
+
265
+ ["Remote Databases:"
266
+ " Use :writer or :remote-peer in your config file to connect via HTTP."
267
+ " Example config with :writer for distributed writes:"
268
+ " {:store {:backend :file :path \"/shared/db\"}"
269
+ " :writer {:backend :datahike-server :url \"http://localhost:4444\" :token \"xyz\"}}"
270
+ " See doc/distributed.md for details."
271
+ ""
272
+ "For more information on a specific command, use: dthk help <command>"]))))
273
+
274
+ ;; =============================================================================
275
+ ;; Argument Parsing and Validation
276
+ ;; =============================================================================
277
+
278
+ (def input-parsers
279
+ "Map of regex patterns to parser functions for handling prefixed inputs.
280
+
281
+ Patterns:
282
+ - conn:file.edn => Connect and return connection
283
+ - db:file.edn => Connect and return database value
284
+ - history:file.edn => Return history database
285
+ - since:timestamp:file.edn => Return database since timestamp
286
+ - asof:timestamp:file.edn => Return database as-of timestamp
287
+ - edn:file.edn => Read EDN from file
288
+ - json:file.edn => Read JSON from file
289
+ - cbor:file.cbor => Read CBOR from file"
290
+ {#"conn:(.+)" #(d/connect (edn/read-string (slurp %)))
291
+ #"db:(.+)" #(deref (d/connect (edn/read-string (slurp %))))
292
+ #"history:(.+)" #(d/history @(d/connect (edn/read-string (slurp %))))
293
+ #"since:(.+):(.+)" #(d/since @(d/connect (edn/read-string (slurp %2)))
294
+ (Date. ^Long (edn/read-string %1)))
295
+ #"asof:(.+):(.+)" #(d/as-of @(d/connect (edn/read-string (slurp %2)))
296
+ (Date. ^Long (edn/read-string %1)))
297
+ #"cbor:(.+)" #(cbor/decode (io/input-stream %))
298
+ #"edn:(.+)" (comp edn/read-string slurp)
299
+ #"json:(.+)" (comp #(j/read-value % json/mapper) slurp)})
300
+
301
+ (defn parse-prefix
302
+ "Parse input prefixes like conn:file.edn, db:file.edn, etc.
303
+ Returns the parsed value or tries to read as EDN if no prefix matches.
304
+
305
+ Examples:
306
+ \"conn:config.edn\" => (d/connect config)
307
+ \"db:config.edn\" => @(d/connect config)
308
+ \"{:x 1}\" => {:x 1}"
309
+ [s]
310
+ (if-not (string? s)
311
+ s ; Already parsed, return as-is
312
+ (if-let [res (reduce (fn [_ [pattern parser-fn]]
313
+ (let [match (re-matches pattern s)]
314
+ (when (first match)
315
+ (reduced (apply parser-fn (rest match))))))
316
+ nil
317
+ input-parsers)]
318
+ res
319
+ ;; No prefix matched - try to parse as EDN
320
+ (try
321
+ (edn/read-string s)
322
+ (catch Exception _
323
+ ;; If EDN parsing fails, return as string
324
+ s)))))
325
+
326
+ (defn validate-args
327
+ "Validate arguments against malli schema.
328
+ Returns {:ok parsed-args} or {:error explanation}"
329
+ [schema args]
330
+ (let [validated (m/validate schema args {:registry types/registry})]
331
+ (if validated
332
+ {:ok args}
333
+ {:error (me/humanize
334
+ (m/explain schema args {:registry types/registry}))})))
335
+
336
+ (defn try-arities
337
+ "Try multiple arities in order until one validates.
338
+ For multi-arity functions, returns {:ok args arity-index} or {:error last-error}"
339
+ [function-schema args]
340
+ (if (and (vector? function-schema) (= :function (first function-schema)))
341
+ ;; Multi-arity: try each arity schema
342
+ (let [arity-schemas (rest function-schema)]
343
+ (loop [schemas arity-schemas
344
+ idx 0]
345
+ (if (empty? schemas)
346
+ {:error "No arity matched the provided arguments"}
347
+ (let [arity-schema (first schemas)
348
+ [_ input-schema _] arity-schema
349
+ result (validate-args input-schema args)]
350
+ (if (:ok result)
351
+ (assoc result :arity idx)
352
+ (recur (rest schemas) (inc idx)))))))
353
+ ;; Single arity
354
+ (validate-args function-schema args)))
355
+
356
+ (defn format-validation-error
357
+ "Format malli validation error for CLI users."
358
+ [op-name schema args error]
359
+ (let [{:keys [doc examples]} (cli-spec op-name)
360
+ example (first examples)]
361
+ (str "❌ Invalid arguments for '" op-name "':\n\n"
362
+ " Expected: " (args-help schema) "\n"
363
+ " Got: " (pr-str args) "\n\n"
364
+ " Error: " (pr-str error) "\n"
365
+ (when example
366
+ (str "\n Example: " (:code example) "\n")))))
367
+
368
+ ;; =============================================================================
369
+ ;; Command Dispatch
370
+ ;; =============================================================================
371
+
372
+ (defn dispatch-command
373
+ "Parse and dispatch a CLI command.
374
+
375
+ Args:
376
+ cmd-parts - Command parts from CLI, e.g. [\"db\" \"exists\"]
377
+ raw-args - Raw argument strings from CLI
378
+ cli-opts - Parsed CLI options (format, verbosity, etc.)
379
+
380
+ Returns the result of calling the operation, or exits on error."
381
+ [cmd-parts raw-args cli-opts]
382
+ (let [cmd-str (str/join " " cmd-parts)
383
+ command-index (build-command-index)
384
+ op-name (get command-index cmd-str)]
385
+
386
+ (if-not op-name
387
+ (do
388
+ (println "❌ Unknown command:" cmd-str)
389
+ (println "\nUse 'dthk --help' to see available commands.")
390
+ (System/exit 1))
391
+
392
+ (let [{:keys [args impl]} (cli-spec op-name)
393
+ ;; Parse arguments (handle prefixes, files, etc.)
394
+ parsed-args (mapv parse-prefix raw-args)
395
+ ;; Validate with malli
396
+ validation (try-arities args parsed-args)]
397
+
398
+ (if (:ok validation)
399
+ ;; Success - call implementation
400
+ ;; impl is a symbol, need to resolve it to a var
401
+ (apply (resolve impl) parsed-args)
402
+
403
+ ;; Validation failed - show error
404
+ (do
405
+ (println (format-validation-error op-name args parsed-args (:error validation)))
406
+ (System/exit 1)))))))
@@ -0,0 +1,291 @@
1
+ (ns datahike.codegen.clj-kondo
2
+ "Generate clj-kondo export config from api.specification.
3
+
4
+ This namespace provides tooling to generate clj-kondo configuration that:
5
+ - Exports all dynamically generated API functions
6
+ - Provides proper arglists for each function
7
+ - Includes documentation strings
8
+ - Provides type information for :type-mismatch linter
9
+ - Enables IDE autocomplete, signature hints, and type checking
10
+
11
+ The generated config allows clj-kondo to understand the dynamically
12
+ expanded API functions from the emit-api macro, eliminating yellow
13
+ squiggles and providing full IDE support."
14
+ (:require [datahike.api.specification :refer [api-specification malli-schema->argslist]]
15
+ [clojure.java.io :as io]
16
+ [clojure.pprint :as pprint]))
17
+
18
+ ;; =============================================================================
19
+ ;; Malli Schema to clj-kondo Type Conversion
20
+ ;; =============================================================================
21
+
22
+ (defn- malli->clj-kondo-type
23
+ "Convert a Malli type to a clj-kondo type keyword.
24
+
25
+ Examples:
26
+ :int -> :int
27
+ :string -> :string
28
+ :boolean -> :boolean
29
+ :datahike/SDB -> :any (custom types become :any)
30
+ [:vector :int] -> :vector
31
+ [:map ...] -> :map"
32
+ [schema]
33
+ (cond
34
+ ;; Primitive types that clj-kondo understands natively
35
+ (#{:int :integer :double :string :boolean :keyword :symbol :nil :any} schema)
36
+ schema
37
+
38
+ ;; Collection types
39
+ (and (vector? schema) (= :vector (first schema)))
40
+ :vector
41
+
42
+ (and (vector? schema) (= :sequential (first schema)))
43
+ :seqable
44
+
45
+ (and (vector? schema) (= :map (first schema)))
46
+ :map
47
+
48
+ (and (vector? schema) (= :set (first schema)))
49
+ :set
50
+
51
+ ;; Function schemas - treat as :ifn
52
+ (and (vector? schema) (#{:=> :function} (first schema)))
53
+ :ifn
54
+
55
+ ;; Or/alt schemas - use :any for now (could be smarter)
56
+ (and (vector? schema) (#{:or :alt} (first schema)))
57
+ :any
58
+
59
+ ;; Maybe/optional - extract inner type
60
+ (and (vector? schema) (= :maybe (first schema)))
61
+ (if-let [inner (second schema)]
62
+ (malli->clj-kondo-type inner)
63
+ :any)
64
+
65
+ ;; Custom types (namespaced keywords like :datahike/SDB) -> :any
66
+ (keyword? schema)
67
+ :any
68
+
69
+ ;; Symbols (type references) -> :any
70
+ (symbol? schema)
71
+ :any
72
+
73
+ ;; Fn predicates [:fn pred] -> :any
74
+ (and (vector? schema) (= :fn (first schema)))
75
+ :any
76
+
77
+ ;; Default
78
+ :else
79
+ :any))
80
+
81
+ (defn- extract-arity-info
82
+ "Extract arity information from a Malli function schema.
83
+
84
+ Input: [:=> [:cat Type1 Type2] RetType]
85
+ Output: {:args [:type1 :type2] :ret :rettype}
86
+
87
+ Input: [:function [:=> [:cat Type1] RetType] [:=> [:cat Type1 Type2] RetType]]
88
+ Output: [{:args [:type1] :ret :rettype}
89
+ {:args [:type1 :type2] :ret :rettype}]"
90
+ [schema]
91
+ (cond
92
+ ;; Single arity: [:=> [:cat Type1 Type2] RetType]
93
+ (and (vector? schema) (= :=> (first schema)))
94
+ (let [[_ input-schema ret-schema] schema]
95
+ (if (and (vector? input-schema) (= :cat (first input-schema)))
96
+ [{:args (mapv malli->clj-kondo-type (rest input-schema))
97
+ :ret (malli->clj-kondo-type ret-schema)}]
98
+ ;; No :cat - assume no args
99
+ [{:args []
100
+ :ret (malli->clj-kondo-type ret-schema)}]))
101
+
102
+ ;; Multi-arity: [:function [:=> ...] [:=> ...]]
103
+ (and (vector? schema) (= :function (first schema)))
104
+ (vec (for [arity-schema (rest schema)]
105
+ (when (and (vector? arity-schema) (= :=> (first arity-schema)))
106
+ (let [[_ input-schema ret-schema] arity-schema]
107
+ (if (and (vector? input-schema) (= :cat (first input-schema)))
108
+ {:args (mapv malli->clj-kondo-type (rest input-schema))
109
+ :ret (malli->clj-kondo-type ret-schema)}
110
+ ;; No :cat - assume no args
111
+ {:args []
112
+ :ret (malli->clj-kondo-type ret-schema)})))))
113
+
114
+ ;; Fallback
115
+ :else
116
+ nil))
117
+
118
+ (defn- generate-type-definition
119
+ "Generate clj-kondo type definition for a single API function.
120
+
121
+ Returns a map with :arities for :type-mismatch linter."
122
+ [fn-name {:keys [args]}]
123
+ (when-let [arities (extract-arity-info args)]
124
+ (let [;; Convert arities to clj-kondo format: {arity {:args [...] :ret ...}}
125
+ arities-map (into {}
126
+ (map-indexed
127
+ (fn [idx {:keys [args ret]}]
128
+ [(count args) {:args args :ret ret}])
129
+ arities))]
130
+ {:arities arities-map})))
131
+
132
+ ;; =============================================================================
133
+ ;; Config Generation
134
+ ;; =============================================================================
135
+
136
+ (defn- generate-var-definition
137
+ "Generate clj-kondo var definition for a single API function.
138
+
139
+ Returns a map with :arglists and :doc for the function."
140
+ [fn-name {:keys [args doc]}]
141
+ (let [arglists (malli-schema->argslist args)
142
+ ;; Ensure arglists is a proper list (not lazy seq or vector)
143
+ arglists-list (if (list? arglists)
144
+ arglists
145
+ (apply list arglists))]
146
+ ;; Use a quoted form (via syntax-quote) so it prints as '(...) not (quote (...))
147
+ {:arglists `'~arglists-list
148
+ :doc doc}))
149
+
150
+ (defn generate-config
151
+ "Generate complete clj-kondo export configuration.
152
+
153
+ Returns an EDN map with:
154
+ - :var-definitions - Basic function signatures for IDE support
155
+ - :linters :type-mismatch - Type information for type checking"
156
+ []
157
+ (let [var-defs (into {}
158
+ (map (fn [[fn-name spec]]
159
+ [fn-name (generate-var-definition fn-name spec)])
160
+ api-specification))
161
+ type-defs (into {}
162
+ (keep (fn [[fn-name spec]]
163
+ (when-let [type-def (generate-type-definition fn-name spec)]
164
+ [fn-name type-def]))
165
+ api-specification))]
166
+ {:var-definitions
167
+ {'datahike.api var-defs}
168
+
169
+ :linters
170
+ {:type-mismatch
171
+ {:namespaces
172
+ {'datahike.api type-defs}}}}))
173
+
174
+ (defn write-config!
175
+ "Write clj-kondo export configuration to file.
176
+
177
+ This should be the resources/ location so it gets packaged in the jar
178
+ and auto-imported by consuming projects.
179
+
180
+ Args:
181
+ output-path - Path to write config.edn (usually resources/clj-kondo.exports/io.replikativ/datahike/config.edn)"
182
+ [output-path]
183
+ (let [config (generate-config)
184
+ output-file (io/file output-path)]
185
+
186
+ ;; Ensure parent directories exist
187
+ (.mkdirs (.getParentFile output-file))
188
+
189
+ ;; Write export config (using prn for now to avoid pprint issues with quoted forms)
190
+ (with-open [writer (io/writer output-file)]
191
+ (binding [*out* writer]
192
+ (println ";; clj-kondo export configuration for Datahike")
193
+ (println ";; AUTO-GENERATED from api-specification")
194
+ (println ";; To regenerate: bb codegen-clj-kondo")
195
+ (println)
196
+ (prn config)))
197
+
198
+ (let [var-count (count (get-in config [:var-definitions 'datahike.api]))
199
+ type-count (count (get-in config [:linters :type-mismatch :namespaces 'datahike.api]))]
200
+ (println (str "Generated clj-kondo export config: " output-path))
201
+ (println (str " Exported " var-count " API functions with signatures"))
202
+ (println (str " Generated " type-count " type definitions for type checking"))
203
+ output-path)))
204
+
205
+ (defn copy-export-to-local!
206
+ "Copy export config to .clj-kondo directory for local development.
207
+
208
+ The export config in resources/ gets packaged in the jar. For local
209
+ development, we also need it in .clj-kondo/datahike/datahike/ which
210
+ is referenced via :config-paths.
211
+
212
+ Args:
213
+ export-path - Source path (resources/clj-kondo.exports/io.replikativ/datahike/config.edn)
214
+ local-path - Destination path (.clj-kondo/datahike/datahike/config.edn)"
215
+ [export-path local-path]
216
+ (let [export-file (io/file export-path)
217
+ local-file (io/file local-path)]
218
+
219
+ ;; Ensure parent directories exist
220
+ (.mkdirs (.getParentFile local-file))
221
+
222
+ ;; Copy file
223
+ (io/copy export-file local-file)
224
+
225
+ (println (str "Copied to local config: " local-path))
226
+ local-path))
227
+
228
+ (defn update-main-config!
229
+ "Update main .clj-kondo/config.edn with type-mismatch linter.
230
+
231
+ Reads existing config, merges in type definitions, and writes back.
232
+ This enables type checking for Datahike's own project (export config
233
+ is for consuming libraries).
234
+
235
+ Args:
236
+ main-config-path - Path to main config (usually .clj-kondo/config.edn)"
237
+ [main-config-path]
238
+ (let [config (generate-config)
239
+ type-defs (get-in config [:linters :type-mismatch :namespaces 'datahike.api])
240
+ main-config-file (io/file main-config-path)
241
+ existing-config (when (.exists main-config-file)
242
+ (read-string (slurp main-config-file)))
243
+
244
+ ;; Merge type-mismatch into existing linters
245
+ updated-config (update-in existing-config
246
+ [:linters :type-mismatch :namespaces]
247
+ (fn [existing]
248
+ (merge existing {'datahike.api type-defs})))]
249
+
250
+ ;; Write updated config with pretty printing
251
+ (with-open [writer (io/writer main-config-file)]
252
+ (binding [*out* writer]
253
+ (println ";; clj-kondo configuration for Datahike")
254
+ (println ";; Type definitions for datahike.api are AUTO-GENERATED")
255
+ (println ";; To regenerate: bb codegen-clj-kondo")
256
+ (println)
257
+ (pprint/pprint updated-config)))
258
+
259
+ (println (str "Updated main config: " main-config-path))
260
+ (println (str " Added type checking for " (count type-defs) " API functions"))
261
+ main-config-path))
262
+
263
+ ;; =============================================================================
264
+ ;; Verification
265
+ ;; =============================================================================
266
+
267
+ (defn verify-config
268
+ "Verify that generated config is up-to-date.
269
+
270
+ Reads existing config and compares to what would be generated.
271
+ Returns true if up-to-date, false otherwise.
272
+
273
+ Useful for CI checks to ensure codegen was run."
274
+ [config-path]
275
+ (try
276
+ (let [expected (generate-config)
277
+ actual (read-string (slurp config-path))]
278
+ (= expected actual))
279
+ (catch Exception e
280
+ (println (str "Error verifying config: " (.getMessage e)))
281
+ false)))
282
+
283
+ (comment
284
+ ;; Generate config
285
+ (write-config! ".clj-kondo/datahike/datahike/config.edn")
286
+
287
+ ;; Verify config is up-to-date
288
+ (verify-config ".clj-kondo/datahike/datahike/config.edn")
289
+
290
+ ;; Preview what would be generated
291
+ (clojure.pprint/pprint (generate-config)))