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,633 @@
1
+ (ns datahike.api.specification
2
+ "Shared specification for different bindings.
3
+
4
+ This namespace holds all semantic information such that individual bindings
5
+ (Clojure API, Java API, JavaScript/TypeScript, HTTP routes, CLI) can be
6
+ automatically derived from it.
7
+
8
+ Following the Proximum pattern - the spec is purely declarative about semantics,
9
+ not about how each binding should look. Names, routes, and method signatures
10
+ are derived via conventions in the codegen modules.
11
+
12
+ Each operation has:
13
+ :args - malli function schema [:=> [:cat ...] ret] or [:function [...]]
14
+ :ret - malli schema for return value
15
+ :doc - documentation string
16
+ :impl - symbol pointing to implementation function
17
+ :categories - semantic grouping tags (vector of keywords)
18
+ :stability - API maturity (:alpha, :beta, :stable)
19
+ :supports-remote? - true if can be exposed via HTTP/remote API
20
+ :referentially-transparent? - true if pure (no side effects, deterministic)
21
+ :examples - structured usage examples (optional)
22
+ :params - detailed parameter documentation (optional)"
23
+ (:require [malli.core :as m]
24
+ [datahike.api.types :as types]))
25
+
26
+ ;; =============================================================================
27
+ ;; Name Derivation Helpers
28
+ ;; =============================================================================
29
+
30
+ (defn ->url
31
+ "Turns an API endpoint name into a URL path segment.
32
+ Removes ? and ! suffixes, uses kebab-case as-is."
33
+ [op-name]
34
+ (-> (str op-name)
35
+ (clojure.string/replace #"[?!]$" "")))
36
+
37
+ (defn ->cli-command
38
+ "Derives CLI command from operation name.
39
+ Examples:
40
+ database-exists? → db-exists
41
+ create-database → db-create
42
+ transact → transact
43
+ q → query"
44
+ [op-name]
45
+ (-> (str op-name)
46
+ (clojure.string/replace #"^database-" "db-")
47
+ (clojure.string/replace #"[?!]$" "")))
48
+
49
+ (defn malli-schema->argslist
50
+ "Extract argument list from malli function schema for defn metadata.
51
+ Handles [:=> [:cat ...] ret] and [:function [...]] schemas."
52
+ [schema]
53
+ (let [form (if (m/schema? schema) (m/form schema) schema)]
54
+ (cond
55
+ ;; [:function [:=> [:cat ...] ret] ...] - multi-arity
56
+ (and (vector? form) (= :function (first form)))
57
+ (for [arity-schema (rest form)]
58
+ (when (and (vector? arity-schema) (= :=> (first arity-schema)))
59
+ (let [[_ input-schema _] arity-schema]
60
+ (if (and (vector? input-schema) (= :cat (first input-schema)))
61
+ (vec (map-indexed (fn [i _] (symbol (str "arg" i)))
62
+ (rest input-schema)))
63
+ []))))
64
+
65
+ ;; [:=> [:cat ...] ret] - single arity
66
+ (and (vector? form) (= :=> (first form)))
67
+ (let [[_ input-schema _] form]
68
+ (if (and (vector? input-schema) (= :cat (first input-schema)))
69
+ (list (vec (map-indexed (fn [i _] (symbol (str "arg" i)))
70
+ (rest input-schema))))
71
+ '([])))
72
+
73
+ :else
74
+ '([& args]))))
75
+
76
+ ;; =============================================================================
77
+ ;; API Specification
78
+ ;; =============================================================================
79
+
80
+ (def api-specification
81
+ "Complete API specification for Datahike.
82
+
83
+ Operation names become:
84
+ - Clojure function names (as-is)
85
+ - Java method names (kebab→camelCase, remove !?, via codegen)
86
+ - JavaScript function names (same as Java)
87
+ - HTTP routes (kebab-case path segments)
88
+ - CLI commands (via ->cli-command)"
89
+
90
+ '{;; =========================================================================
91
+ ;; Database Lifecycle
92
+ ;; =========================================================================
93
+
94
+ database-exists?
95
+ {:args [:function
96
+ [:=> [:cat :datahike/SConfig] :boolean]
97
+ [:=> [:cat] :boolean]]
98
+ :ret :boolean
99
+ :categories [:database :lifecycle :query]
100
+ :stability :stable
101
+ :supports-remote? true
102
+ :referentially-transparent? false
103
+ :doc "Checks if a database exists via configuration map."
104
+ :examples [{:desc "Check if in-memory database exists"
105
+ :code "(database-exists? {:store {:backend :memory :id \"example\"}})"}
106
+ {:desc "Check with default config"
107
+ :code "(database-exists?)"}]
108
+ :impl datahike.api.impl/database-exists?}
109
+
110
+ create-database
111
+ {:args [:function
112
+ [:=> [:cat :datahike/SConfig] :datahike/SConfig]
113
+ [:=> [:cat] :datahike/SConfig]]
114
+ :ret :datahike/SConfig
115
+ :categories [:database :lifecycle :write]
116
+ :stability :stable
117
+ :supports-remote? true
118
+ :referentially-transparent? false
119
+ :doc "Creates a database via configuration map."
120
+ :examples [{:desc "Create empty database"
121
+ :code "(create-database {:store {:backend :memory :id \"example\"}})"}
122
+ {:desc "Create with schema-flexibility :read"
123
+ :code "(create-database {:store {:backend :memory :id \"example\"} :schema-flexibility :read})"}
124
+ {:desc "Create without history"
125
+ :code "(create-database {:store {:backend :memory :id \"example\"} :keep-history? false})"}
126
+ {:desc "Create with initial schema"
127
+ :code "(create-database {:store {:backend :memory :id \"example\"}
128
+ :initial-tx [{:db/ident :name
129
+ :db/valueType :db.type/string
130
+ :db/cardinality :db.cardinality/one}]})"}]
131
+ :impl datahike.api.impl/create-database}
132
+
133
+ delete-database
134
+ {:args [:function
135
+ [:=> [:cat :datahike/SConfig] :any]
136
+ [:=> [:cat] :any]]
137
+ :ret :any
138
+ :categories [:database :lifecycle :write]
139
+ :stability :stable
140
+ :supports-remote? true
141
+ :referentially-transparent? false
142
+ :doc "Deletes a database given via configuration map."
143
+ :examples [{:desc "Delete database"
144
+ :code "(delete-database {:store {:backend :memory :id \"example\"}})"}]
145
+ :impl datahike.api.impl/delete-database}
146
+
147
+ ;; =========================================================================
148
+ ;; Connection Lifecycle
149
+ ;; =========================================================================
150
+
151
+ connect
152
+ {:args [:function
153
+ [:=> [:cat :datahike/SConfig] :datahike/SConnection]
154
+ [:=> [:cat :datahike/SConfig :map] :datahike/SConnection]
155
+ [:=> [:cat] :datahike/SConnection]]
156
+ :ret :datahike/SConnection
157
+ :categories [:connection :lifecycle]
158
+ :stability :stable
159
+ :supports-remote? true
160
+ :referentially-transparent? false
161
+ :doc "Connects to a Datahike database via configuration map."
162
+ :examples [{:desc "Connect to default in-memory database"
163
+ :code "(connect)"}
164
+ {:desc "Connect to file-based database"
165
+ :code "(connect {:store {:backend :file :path \"/tmp/example\"}})"}
166
+ {:desc "Connect with options"
167
+ :code "(connect {:store {:backend :memory :id \"example\"}} {:validate? true})"}]
168
+ :impl datahike.connector/connect}
169
+
170
+ db
171
+ {:args [:=> [:cat :datahike/SConnection] :datahike/SDB]
172
+ :ret :datahike/SDB
173
+ :categories [:connection :query]
174
+ :stability :stable
175
+ :supports-remote? true
176
+ :referentially-transparent? false
177
+ :doc "Returns the underlying immutable database value from a connection. Prefer using @conn directly."
178
+ :examples [{:desc "Get database from connection"
179
+ :code "(db conn)"}
180
+ {:desc "Prefer direct deref"
181
+ :code "@conn"}]
182
+ :impl datahike.api.impl/db}
183
+
184
+ release
185
+ {:args [:=> [:cat :datahike/SConnection] :nil]
186
+ :ret :nil
187
+ :categories [:connection :lifecycle]
188
+ :stability :stable
189
+ :supports-remote? true
190
+ :referentially-transparent? false
191
+ :doc "Releases a database connection."
192
+ :examples [{:desc "Release connection"
193
+ :code "(release conn)"}]
194
+ :impl datahike.connector/release}
195
+
196
+ ;; =========================================================================
197
+ ;; Transaction Operations
198
+ ;; =========================================================================
199
+
200
+ transact
201
+ {:args [:=> [:cat :datahike/SConnection :datahike/STransactions] :datahike/STransactionReport]
202
+ :ret :datahike/STransactionReport
203
+ :categories [:transaction :write]
204
+ :stability :stable
205
+ :supports-remote? true
206
+ :referentially-transparent? false
207
+ :doc "Applies transaction to the database and updates connection."
208
+ :examples [{:desc "Add single datom"
209
+ :code "(transact conn [[:db/add 1 :name \"Ivan\"]])"}
210
+ {:desc "Retract datom"
211
+ :code "(transact conn [[:db/retract 1 :name \"Ivan\"]])"}
212
+ {:desc "Create entity with tempid"
213
+ :code "(transact conn [[:db/add -1 :name \"Ivan\"]])"}
214
+ {:desc "Create entity (map form)"
215
+ :code "(transact conn [{:db/id -1 :name \"Ivan\" :likes [\"fries\" \"pizza\"]}])"}
216
+ {:desc "Read from stdin (CLI)"
217
+ :cli "cat data.edn | dthk transact conn:config.edn -"}]
218
+ :impl datahike.api.impl/transact}
219
+
220
+ transact!
221
+ {:args [:=> [:cat :datahike/SConnection :datahike/STransactions] :any]
222
+ :ret :any
223
+ :categories [:transaction :write :async]
224
+ :stability :stable
225
+ :supports-remote? false
226
+ :referentially-transparent? false
227
+ :doc "Same as transact, but asynchronously returns a future."
228
+ :examples [{:desc "Async transaction"
229
+ :code "@(transact! conn [{:db/id -1 :name \"Alice\"}])"}]
230
+ :impl datahike.api.impl/transact!}
231
+
232
+ load-entities
233
+ {:args [:=> [:cat :datahike/SConnection :datahike/STransactions] :any]
234
+ :ret :any
235
+ :categories [:transaction :write :bulk]
236
+ :stability :stable
237
+ :supports-remote? true
238
+ :referentially-transparent? false
239
+ :doc "Load entities directly (bulk load)."
240
+ :examples [{:desc "Bulk load entities"
241
+ :code "(load-entities conn entities)"}]
242
+ :impl datahike.writer/load-entities}
243
+
244
+ with
245
+ {:args [:function
246
+ [:=> [:cat :datahike/SDB :datahike/SWithArgs] :datahike/STransactionReport]
247
+ [:=> [:cat :datahike/SDB :datahike/STransactions] :datahike/STransactionReport]
248
+ [:=> [:cat :datahike/SDB :datahike/STransactions :datahike/STxMeta] :datahike/STransactionReport]]
249
+ :ret :datahike/STransactionReport
250
+ :categories [:transaction :immutable]
251
+ :stability :stable
252
+ :supports-remote? false
253
+ :referentially-transparent? true
254
+ :doc "Applies transaction to immutable db value. Returns transaction report."
255
+ :examples [{:desc "Transaction on db value"
256
+ :code "(with @conn [[:db/add 1 :name \"Ivan\"]])"}
257
+ {:desc "With metadata"
258
+ :code "(with @conn {:tx-data [...] :tx-meta {:source :import}})"}]
259
+ :impl datahike.api.impl/with}
260
+
261
+ db-with
262
+ {:args [:=> [:cat :datahike/SDB :datahike/STransactions] :datahike/SDB]
263
+ :ret :datahike/SDB
264
+ :categories [:transaction :immutable]
265
+ :stability :stable
266
+ :supports-remote? false
267
+ :referentially-transparent? true
268
+ :doc "Applies transaction to immutable db value, returns new db. Same as (:db-after (with db tx-data))."
269
+ :examples [{:desc "Get db after transaction"
270
+ :code "(db-with @conn [[:db/add 1 :name \"Ivan\"]])"}]
271
+ :impl datahike.api.impl/db-with}
272
+
273
+ ;; =========================================================================
274
+ ;; Query Operations
275
+ ;; =========================================================================
276
+
277
+ q
278
+ {:args [:function
279
+ [:=> [:cat :datahike/SQueryArgs] :any]
280
+ [:=> [:cat [:or [:vector :any] :map :string] [:* :any]] :any]]
281
+ :ret :any
282
+ :categories [:query]
283
+ :stability :stable
284
+ :supports-remote? true
285
+ :referentially-transparent? true
286
+ :doc "Executes a datalog query."
287
+ :examples [{:desc "Query with vector syntax"
288
+ :code "(q '[:find ?value :where [_ :likes ?value]] db)"}
289
+ {:desc "Query with map syntax"
290
+ :code "(q '{:find [?value] :where [[_ :likes ?value]]} db)"}
291
+ {:desc "Query with pagination"
292
+ :code "(q {:query '[:find ?value :where [_ :likes ?value]]
293
+ :args [db]
294
+ :offset 2
295
+ :limit 10})"}]
296
+ :impl datahike.query/q}
297
+
298
+ query-stats
299
+ {:args [:function
300
+ [:=> [:cat :datahike/SQueryArgs] :map]
301
+ [:=> [:cat [:or [:vector :any] :map] [:* :any]] :map]]
302
+ :ret :map
303
+ :categories [:query :diagnostics]
304
+ :stability :stable
305
+ :supports-remote? true
306
+ :referentially-transparent? true
307
+ :doc "Executes query and returns execution statistics."
308
+ :examples [{:desc "Query with stats"
309
+ :code "(query-stats '[:find ?e :where [?e :name]] db)"}]
310
+ :impl datahike.query/query-stats}
311
+
312
+ pull
313
+ {:args [:function
314
+ [:=> [:cat :datahike/SDB :datahike/SPullOptions] [:maybe :map]]
315
+ [:=> [:cat :datahike/SDB [:vector :any] :datahike/SEId] [:maybe :map]]]
316
+ :ret [:maybe :map]
317
+ :categories [:query :pull]
318
+ :stability :stable
319
+ :supports-remote? true
320
+ :referentially-transparent? true
321
+ :doc "Fetches data using recursive declarative pull pattern."
322
+ :examples [{:desc "Pull with pattern"
323
+ :code "(pull db [:db/id :name :likes {:friends [:db/id :name]}] 1)"}
324
+ {:desc "Pull with arg-map"
325
+ :code "(pull db {:selector [:db/id :name] :eid 1})"}]
326
+ :impl datahike.pull-api/pull}
327
+
328
+ pull-many
329
+ {:args [:function
330
+ [:=> [:cat :datahike/SDB :datahike/SPullOptions] [:sequential :map]]
331
+ [:=> [:cat :datahike/SDB [:vector :any] :datahike/SEId] [:sequential :map]]]
332
+ :ret [:sequential :map]
333
+ :categories [:query :pull]
334
+ :stability :stable
335
+ :supports-remote? true
336
+ :referentially-transparent? true
337
+ :doc "Same as pull, but accepts sequence of ids and returns sequence of maps."
338
+ :examples [{:desc "Pull multiple entities"
339
+ :code "(pull-many db [:db/id :name] [1 2 3])"}]
340
+ :impl datahike.pull-api/pull-many}
341
+
342
+ entity
343
+ {:args [:=> [:cat :datahike/SDB [:or :datahike/SEId :any]] :any]
344
+ :ret :any
345
+ :categories [:query :entity]
346
+ :stability :stable
347
+ :supports-remote? true
348
+ :referentially-transparent? true
349
+ :doc "Retrieves an entity by its id. Returns lazy map-like structure."
350
+ :examples [{:desc "Get entity by id"
351
+ :code "(entity db 1)"}
352
+ {:desc "Get entity by lookup ref"
353
+ :code "(entity db [:email \"alice@example.com\"])"}
354
+ {:desc "Navigate entity attributes"
355
+ :code "(:name (entity db 1))"}]
356
+ :impl datahike.impl.entity/entity}
357
+
358
+ entity-db
359
+ {:args [:=> [:cat :any] :datahike/SDB]
360
+ :ret :datahike/SDB
361
+ :categories [:query :entity]
362
+ :stability :stable
363
+ :supports-remote? true
364
+ :referentially-transparent? true
365
+ :doc "Returns database that entity was created from."
366
+ :examples [{:desc "Get entity's database"
367
+ :code "(entity-db (entity db 1))"}]
368
+ :impl datahike.impl.entity/entity-db}
369
+
370
+ ;; =========================================================================
371
+ ;; Index Operations
372
+ ;; =========================================================================
373
+
374
+ datoms
375
+ {:args [:function
376
+ [:=> [:cat :datahike/SDB :datahike/SIndexLookupArgs] [:maybe :datahike/SDatoms]]
377
+ [:=> [:cat :datahike/SDB :keyword [:* :any]] [:maybe :datahike/SDatoms]]]
378
+ :ret [:maybe :datahike/SDatoms]
379
+ :categories [:query :index :advanced]
380
+ :stability :stable
381
+ :supports-remote? true
382
+ :referentially-transparent? true
383
+ :doc "Index lookup. Returns sequence of datoms matching index components."
384
+ :examples [{:desc "Find all datoms for entity"
385
+ :code "(datoms db {:index :eavt :components [1]})"}
386
+ {:desc "Find datoms for entity and attribute"
387
+ :code "(datoms db {:index :eavt :components [1 :likes]})"}
388
+ {:desc "Find by attribute and value (requires :db/index)"
389
+ :code "(datoms db {:index :avet :components [:likes \"pizza\"]})"}]
390
+ :impl datahike.api.impl/datoms}
391
+
392
+ seek-datoms
393
+ {:args [:function
394
+ [:=> [:cat :datahike/SDB :datahike/SIndexLookupArgs] [:maybe :datahike/SDatoms]]
395
+ [:=> [:cat :datahike/SDB :keyword [:* :any]] [:maybe :datahike/SDatoms]]]
396
+ :ret [:maybe :datahike/SDatoms]
397
+ :categories [:query :index :advanced]
398
+ :stability :stable
399
+ :supports-remote? true
400
+ :referentially-transparent? true
401
+ :doc "Like datoms, but returns datoms starting from specified components through end of index."
402
+ :examples [{:desc "Seek from entity"
403
+ :code "(seek-datoms db {:index :eavt :components [1]})"}]
404
+ :impl datahike.api.impl/seek-datoms}
405
+
406
+ index-range
407
+ {:args [:=> [:cat :datahike/SDB :datahike/SIndexRangeArgs] :datahike/SDatoms]
408
+ :ret :datahike/SDatoms
409
+ :categories [:query :index :advanced]
410
+ :stability :stable
411
+ :supports-remote? true
412
+ :referentially-transparent? true
413
+ :doc "Returns part of :avet index between start and end values."
414
+ :examples [{:desc "Find datoms in value range"
415
+ :code "(index-range db {:attrid :likes :start \"a\" :end \"z\"})"}
416
+ {:desc "Find entities with age in range"
417
+ :code "(->> (index-range db {:attrid :age :start 18 :end 60}) (map :e))"}]
418
+ :impl datahike.api.impl/index-range}
419
+
420
+ ;; =========================================================================
421
+ ;; Database Filtering
422
+ ;; =========================================================================
423
+
424
+ filter
425
+ {:args [:=> [:cat :datahike/SDB :any] :datahike/SDB]
426
+ :ret :datahike/SDB
427
+ :categories [:query :filter]
428
+ :stability :stable
429
+ :supports-remote? false
430
+ :referentially-transparent? true
431
+ :doc "Returns filtered view over database. Only includes datoms where (pred db datom) is true."
432
+ :examples [{:desc "Filter to recent datoms"
433
+ :code "(filter db (fn [db datom] (> (:tx datom) recent-tx)))"}]
434
+ :impl datahike.core/filter}
435
+
436
+ is-filtered
437
+ {:args [:=> [:cat :datahike/SDB] :boolean]
438
+ :ret :boolean
439
+ :categories [:query :filter]
440
+ :stability :stable
441
+ :supports-remote? false
442
+ :referentially-transparent? true
443
+ :doc "Returns true if database was filtered using filter, false otherwise."
444
+ :examples [{:desc "Check if filtered"
445
+ :code "(is-filtered db)"}]
446
+ :impl datahike.core/is-filtered}
447
+
448
+ ;; =========================================================================
449
+ ;; Temporal Queries
450
+ ;; =========================================================================
451
+
452
+ history
453
+ {:args [:=> [:cat :datahike/SDB] :any]
454
+ :ret :any
455
+ :categories [:temporal :query]
456
+ :stability :stable
457
+ :supports-remote? true
458
+ :referentially-transparent? true
459
+ :doc "Returns full historical state of database including all assertions and retractions."
460
+ :examples [{:desc "Query historical data"
461
+ :code "(q '[:find ?n ?a :where [?e :name ?n] [?e :age ?a]] (history @conn))"}]
462
+ :impl datahike.api.impl/history}
463
+
464
+ since
465
+ {:args [:=> [:cat :datahike/SDB types/time-point?] :datahike/SDB]
466
+ :ret :datahike/SDB
467
+ :categories [:temporal :query]
468
+ :stability :stable
469
+ :supports-remote? true
470
+ :referentially-transparent? true
471
+ :doc "Returns database state since given time point (Date or transaction ID). Contains only datoms added since that point."
472
+ :examples [{:desc "Query since date"
473
+ :code "(since @conn (java.util.Date.))"}
474
+ {:desc "Query since transaction"
475
+ :code "(since @conn 536870913)"}]
476
+ :impl datahike.api.impl/since}
477
+
478
+ as-of
479
+ {:args [:=> [:cat :datahike/SDB types/time-point?] :datahike/SDB]
480
+ :ret :datahike/SDB
481
+ :categories [:temporal :query]
482
+ :stability :stable
483
+ :supports-remote? true
484
+ :referentially-transparent? true
485
+ :doc "Returns database state at given time point (Date or transaction ID)."
486
+ :examples [{:desc "Query as of date"
487
+ :code "(q '[:find ?n :where [_ :name ?n]] (as-of @conn date))"}
488
+ {:desc "Query as of transaction"
489
+ :code "(as-of @conn 536870913)"}]
490
+ :impl datahike.api.impl/as-of}
491
+
492
+ ;; =========================================================================
493
+ ;; Reactive Operations
494
+ ;; =========================================================================
495
+
496
+ listen
497
+ {:args [:function
498
+ [:=> [:cat :datahike/SConnection :any] :any]
499
+ [:=> [:cat :datahike/SConnection :any :any] :any]]
500
+ :ret :any
501
+ :categories [:connection :reactive]
502
+ :stability :stable
503
+ :supports-remote? false
504
+ :referentially-transparent? false
505
+ :doc "Listen for changes on connection. Callback called with transaction report on each transact."
506
+ :examples [{:desc "Listen with callback"
507
+ :code "(listen conn (fn [tx-report] (println \"Transaction:\" (:tx-data tx-report))))"}
508
+ {:desc "Listen with key"
509
+ :code "(listen conn :my-listener (fn [tx-report] ...))"}]
510
+ :impl datahike.core/listen!}
511
+
512
+ unlisten
513
+ {:args [:=> [:cat :datahike/SConnection :any] :map]
514
+ :ret :map
515
+ :categories [:connection :reactive]
516
+ :stability :stable
517
+ :supports-remote? false
518
+ :referentially-transparent? false
519
+ :doc "Removes registered listener from connection."
520
+ :examples [{:desc "Remove listener"
521
+ :code "(unlisten conn :my-listener)"}]
522
+ :impl datahike.core/unlisten!}
523
+
524
+ ;; =========================================================================
525
+ ;; Schema Operations
526
+ ;; =========================================================================
527
+
528
+ schema
529
+ {:args [:=> [:cat :datahike/SDB] :datahike/SSchema]
530
+ :ret :datahike/SSchema
531
+ :categories [:schema :query]
532
+ :stability :stable
533
+ :supports-remote? true
534
+ :referentially-transparent? true
535
+ :doc "Returns current schema definition."
536
+ :examples [{:desc "Get schema"
537
+ :code "(schema @conn)"}]
538
+ :impl datahike.api.impl/schema}
539
+
540
+ reverse-schema
541
+ {:args [:=> [:cat :datahike/SDB] :map]
542
+ :ret :map
543
+ :categories [:schema :query]
544
+ :stability :stable
545
+ :supports-remote? true
546
+ :referentially-transparent? true
547
+ :doc "Returns reverse schema definition (attribute id to ident mapping)."
548
+ :examples [{:desc "Get reverse schema"
549
+ :code "(reverse-schema @conn)"}]
550
+ :impl datahike.api.impl/reverse-schema}
551
+
552
+ ;; =========================================================================
553
+ ;; Diagnostics & Maintenance
554
+ ;; =========================================================================
555
+
556
+ metrics
557
+ {:args [:=> [:cat :datahike/SDB] :datahike/SMetrics]
558
+ :ret :datahike/SMetrics
559
+ :categories [:diagnostics :query]
560
+ :stability :stable
561
+ :supports-remote? true
562
+ :referentially-transparent? true
563
+ :doc "Returns database metrics (datom counts, index sizes, etc)."
564
+ :examples [{:desc "Get metrics"
565
+ :code "(metrics @conn)"}]
566
+ :impl datahike.db/metrics}
567
+
568
+ gc-storage
569
+ {:args [:function
570
+ [:=> [:cat :datahike/SConnection types/time-point?] :any]
571
+ [:=> [:cat :datahike/SConnection] :any]]
572
+ :ret :any
573
+ :categories [:maintenance :lifecycle]
574
+ :stability :stable
575
+ :supports-remote? true
576
+ :referentially-transparent? false
577
+ :doc "Invokes garbage collection on connection's store. Removes old snapshots before given time point."
578
+ :examples [{:desc "GC all old snapshots"
579
+ :code "(gc-storage conn)"}
580
+ {:desc "GC snapshots before date"
581
+ :code "(gc-storage conn (java.util.Date.))"}]
582
+ :impl datahike.writer/gc-storage!}
583
+
584
+ ;; =========================================================================
585
+ ;; Utility Operations
586
+ ;; =========================================================================
587
+
588
+ tempid
589
+ {:args [:function
590
+ [:=> [:cat :any] neg-int?]
591
+ [:=> [:cat :any :int] :int]]
592
+ :ret [:or neg-int? :int]
593
+ :categories [:utility]
594
+ :stability :stable
595
+ :supports-remote? false
596
+ :referentially-transparent? true
597
+ :doc "Allocates temporary id (negative integer). Prefer using negative integers directly."
598
+ :examples [{:desc "Generate tempid"
599
+ :code "(tempid :db.part/user)"}
600
+ {:desc "Prefer direct negative integers"
601
+ :code "(transact conn [{:db/id -1 :name \"Alice\"}])"}]
602
+ :impl datahike.core/tempid}})
603
+
604
+ ;; =============================================================================
605
+ ;; Helper Functions
606
+ ;; =============================================================================
607
+
608
+ (defn pure-operations
609
+ "Returns operations that are referentially transparent (pure functions)."
610
+ []
611
+ (filter (fn [[_ spec]] (:referentially-transparent? spec)) api-specification))
612
+
613
+ (defn io-operations
614
+ "Returns operations with side effects (I/O operations)."
615
+ []
616
+ (remove (fn [[_ spec]] (:referentially-transparent? spec)) api-specification))
617
+
618
+ (defn remote-operations
619
+ "Returns operations that support remote access (HTTP)."
620
+ []
621
+ (filter (fn [[_ spec]] (:supports-remote? spec)) api-specification))
622
+
623
+ (defn local-only-operations
624
+ "Returns operations that must run locally."
625
+ []
626
+ (remove (fn [[_ spec]] (:supports-remote? spec)) api-specification))
627
+
628
+ (defn operations-by-category
629
+ "Returns operations grouped by category."
630
+ [category]
631
+ (filter (fn [[_ spec]]
632
+ (some #(= % category) (:categories spec)))
633
+ api-specification))