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,488 @@
1
+ (ns datahike.codegen.pod
2
+ "Generate Babashka pod bindings from API specification.
3
+
4
+ This namespace provides pod-specific configuration and logic for:
5
+ - Mapping API operations to pod functions with ID-based references
6
+ - Managing connection and database caching
7
+ - Generating describe-map for pod protocol
8
+ - Handling argument resolution and result transforms"
9
+ (:require [clojure.set :as set]
10
+ [clojure.string :as str]
11
+ [datahike.api.specification :refer [api-specification]]))
12
+
13
+ ;; =============================================================================
14
+ ;; Pod-Specific Configuration (Overlay)
15
+ ;; =============================================================================
16
+
17
+ (def pod-operations
18
+ "Pod-specific configuration extending the universal API specification.
19
+
20
+ Keys:
21
+ - :resolve - Map of arg positions to resolver type (:conn, :db)
22
+ - :returns - What the function returns (:conn-id, :db-id, :value)
23
+ - :transform - Named transform to apply to result
24
+ - :post-action - Action after main operation (:remove-conn)
25
+ - :custom-fn - Custom function body (quoted) for operations needing special handling"
26
+
27
+ '{;; Database lifecycle - direct pass-through
28
+ database-exists? {:returns :value}
29
+ create-database {:returns :value}
30
+ delete-database {:returns :value}
31
+
32
+ ;; Connection lifecycle
33
+ connect {:returns :conn-id}
34
+ release {:resolve {0 :conn}
35
+ :returns :value
36
+ :post-action :remove-conn}
37
+
38
+ ;; DB access
39
+ db {:resolve {0 :conn}
40
+ :returns :db-id}
41
+
42
+ ;; Temporal - derive from db
43
+ as-of {:resolve {0 :db} :returns :db-id}
44
+ since {:resolve {0 :db} :returns :db-id}
45
+ history {:resolve {0 :db} :returns :db-id}
46
+ db-with {:resolve {0 :db} :returns :db-id}
47
+
48
+ ;; Query - has custom implementation due to multiple calling conventions
49
+ q {:custom-fn
50
+ (fn [& args]
51
+ (let [resolve-db-arg (fn [arg]
52
+ (if-let [entry (get @dbs arg)]
53
+ (:db entry)
54
+ arg))]
55
+ (if (map? (first args))
56
+ ;; Map syntax: {:query ... :args [...]}
57
+ (d/q (update (first args) :args (fn [a] (mapv resolve-db-arg a))))
58
+ ;; Vector syntax: query db & args
59
+ (apply d/q (first args) (mapv resolve-db-arg (rest args))))))}
60
+
61
+ ;; DB consumers - resolve db, return value
62
+ pull {:resolve {0 :db} :returns :value}
63
+ pull-many {:resolve {0 :db} :returns :value}
64
+ entity {:resolve {0 :db} :returns :value :transform :entity->map}
65
+ datoms {:resolve {0 :db} :returns :value :transform :datoms->seqs}
66
+ seek-datoms {:resolve {0 :db} :returns :value :transform :datoms->seqs}
67
+ schema {:resolve {0 :db} :returns :value}
68
+ reverse-schema {:resolve {0 :db} :returns :value}
69
+ metrics {:resolve {0 :db} :returns :value}
70
+
71
+ ;; Connection consumers
72
+ transact {:resolve {0 :conn}
73
+ :returns :value
74
+ :transform :tx-report->summary}})
75
+
76
+ (def pod-excluded-operations
77
+ "Operations excluded from pod with documented reasons."
78
+ '{listen "Requires persistent callbacks - not supported in pod protocol"
79
+ unlisten "Requires persistent callbacks - not supported in pod protocol"
80
+ transact! "Async variant - pods are synchronous"
81
+ tempid "Only useful within transaction context"
82
+ entity-db "Returns DB from entity - limited utility in pod context"
83
+ filter "Requires function argument - cannot serialize across pod boundary"
84
+ is-filtered "Limited utility in pod context"
85
+ with "Returns tx-report with db objects - use db-with instead"
86
+ load-entities "Batch loading - use transact instead"
87
+ query-stats "Not yet exposed in pod"
88
+ index-range "Advanced index operation - can add later"
89
+ gc-storage "Maintenance operation - can add later"})
90
+
91
+ (def pod-additions
92
+ "Pod-specific operations not in main API specification."
93
+ '{release-db {:doc "Release a cached database snapshot from pod memory.
94
+
95
+ Takes the db-id that was returned from db, as-of, since, or history.
96
+ Returns an empty map."
97
+ :args [:db-id]
98
+ :returns :value}})
99
+
100
+ (def with-db-macro-code
101
+ "Client-side macro for automatic db cleanup."
102
+ "(defmacro with-db [bindings & body]
103
+ (cond
104
+ (= (count bindings) 0) `(do ~@body)
105
+ (symbol? (bindings 0)) `(let ~(subvec bindings 0 2)
106
+ (try
107
+ (with-db ~(subvec bindings 2) ~@body)
108
+ (finally
109
+ (release-db ~(bindings 0)))))
110
+ :else (throw (IllegalArgumentException.
111
+ \"with-db only allows Symbols in bindings\"))))")
112
+
113
+ ;; =============================================================================
114
+ ;; Transform Registry
115
+ ;; =============================================================================
116
+
117
+ (def transforms
118
+ "Named transforms for pod result processing.
119
+ Values are quoted forms that will be inlined in generated code."
120
+ '{:entity->map
121
+ (fn [e] (reduce-kv #(assoc %1 %2 %3) {} e))
122
+
123
+ :datoms->seqs
124
+ (fn [datoms] (map seq datoms))
125
+
126
+ :tx-report->summary
127
+ (fn [{:keys [db-before db-after tx-meta tx-data tempids]}]
128
+ {:tempids tempids
129
+ :db-before (select-keys db-before [:max-tx :max-eid])
130
+ :db-after (select-keys db-after [:max-tx :max-eid])
131
+ :tx-meta tx-meta
132
+ :tx-data (map seq tx-data)})})
133
+
134
+ ;; =============================================================================
135
+ ;; Arity Extraction from Malli Schemas
136
+ ;; =============================================================================
137
+
138
+ (defn extract-arities
139
+ "Extract argument arities from malli function schema.
140
+ Returns vector of arity vectors, e.g. [[] [config] [config opts]]"
141
+ [schema]
142
+ (cond
143
+ ;; [:function [:=> [:cat ...] ret] [:=> [:cat ...] ret] ...]
144
+ (and (vector? schema) (= :function (first schema)))
145
+ (vec (for [arity-schema (rest schema)
146
+ :when (and (vector? arity-schema) (= :=> (first arity-schema)))]
147
+ (let [[_ input-schema _] arity-schema]
148
+ (if (and (vector? input-schema) (= :cat (first input-schema)))
149
+ (vec (rest input-schema))
150
+ []))))
151
+
152
+ ;; [:=> [:cat ...] ret]
153
+ (and (vector? schema) (= :=> (first schema)))
154
+ (let [[_ input-schema _] schema]
155
+ (if (and (vector? input-schema) (= :cat (first input-schema)))
156
+ [(vec (rest input-schema))]
157
+ [[]]))
158
+
159
+ :else
160
+ [[]]))
161
+
162
+ (defn variadic-schema?
163
+ "Check if a schema element is a variadic marker like [:* :any]"
164
+ [schema-elem]
165
+ (and (vector? schema-elem)
166
+ (= :* (first schema-elem))))
167
+
168
+ (defn arity-arg-names
169
+ "Generate argument names for an arity.
170
+ Returns vector of symbols like [arg0 arg1 arg2] or [arg0 arg1 & args] for variadic.
171
+ Also returns metadata {:variadic? bool}"
172
+ [arity-schema]
173
+ (let [;; Check if last element is variadic
174
+ has-variadic? (and (seq arity-schema)
175
+ (variadic-schema? (last arity-schema)))
176
+ ;; Fixed args are all but the variadic marker
177
+ fixed-schema (if has-variadic?
178
+ (butlast arity-schema)
179
+ arity-schema)
180
+ fixed-names (vec (map-indexed (fn [i _] (symbol (str "arg" i))) fixed-schema))]
181
+ (if has-variadic?
182
+ {:args (vec (concat fixed-names ['& 'rest-args]))
183
+ :variadic? true
184
+ :fixed-count (count fixed-names)}
185
+ {:args fixed-names
186
+ :variadic? false
187
+ :fixed-count (count fixed-names)})))
188
+
189
+ ;; =============================================================================
190
+ ;; Code Generation - Simplified Approach
191
+ ;; =============================================================================
192
+
193
+ (defn wrap-with-transform
194
+ "Wrap result expression with transform if specified."
195
+ [transform result-expr]
196
+ (if transform
197
+ (list (get transforms transform) result-expr)
198
+ result-expr))
199
+
200
+ ;; =============================================================================
201
+ ;; Function Generation
202
+ ;; =============================================================================
203
+
204
+ (defn generate-api-call
205
+ "Generate the API call expression, handling variadic args with apply."
206
+ [api-impl fixed-args variadic?]
207
+ (if variadic?
208
+ `(apply ~api-impl ~@fixed-args ~'rest-args)
209
+ `(~api-impl ~@fixed-args)))
210
+
211
+ (defn generate-arity-body
212
+ "Generate the body for a single arity of a pod function.
213
+ Uses explicit symbols instead of gensyms for clarity in generated code.
214
+ arity-info is {:args [...] :variadic? bool :fixed-count n}"
215
+ [op-name overlay arity-info api-impl]
216
+ (let [{:keys [resolve returns transform post-action]} overlay
217
+ {:keys [args variadic? fixed-count]} arity-info
218
+ ;; Extract just the named args (excluding & and rest-args)
219
+ named-args (if variadic?
220
+ (take fixed-count args)
221
+ args)
222
+ ;; Symbols for generated code
223
+ result-sym 'result
224
+ db-id-sym 'db-id
225
+ conn-id-sym 'conn-id
226
+ ;; First arg is typically the one being resolved
227
+ first-arg (first named-args)
228
+ rest-named-args (rest named-args)
229
+ ;; Check what kind of resolution we need
230
+ resolves-conn? (= :conn (get resolve 0))
231
+ resolves-db? (= :db (get resolve 0))]
232
+
233
+ (cond
234
+ ;; Case: resolve conn, return conn-id (connect)
235
+ (and resolves-conn? (= returns :conn-id))
236
+ `(let [~result-sym ~(generate-api-call api-impl named-args variadic?)
237
+ ~conn-id-sym (~'generate-conn-id ~(or first-arg {}))]
238
+ (swap! ~'conns assoc ~conn-id-sym ~result-sym)
239
+ ~conn-id-sym)
240
+
241
+ ;; Case: resolve conn, return db-id (db from conn)
242
+ (and resolves-conn? (= returns :db-id))
243
+ `(let [~'conn (~'resolve-conn ~first-arg)
244
+ ~result-sym (~'d/db ~'conn)
245
+ ~db-id-sym (~'generate-db-id ~result-sym)]
246
+ (swap! ~'dbs assoc ~db-id-sym {:db ~result-sym :conn-id ~first-arg})
247
+ ~db-id-sym)
248
+
249
+ ;; Case: resolve conn, return value (transact, release)
250
+ (and resolves-conn? (= returns :value))
251
+ (let [call-expr (if variadic?
252
+ `(apply ~api-impl ~'conn ~@rest-named-args ~'rest-args)
253
+ `(~api-impl ~'conn ~@rest-named-args))
254
+ call-with-resolved `(let [~'conn (~'resolve-conn ~first-arg)
255
+ ~result-sym ~call-expr]
256
+ ~(wrap-with-transform transform result-sym))]
257
+ (if post-action
258
+ `(let [~'ret ~call-with-resolved]
259
+ ~(case post-action
260
+ :remove-conn `(swap! ~'conns dissoc ~first-arg)
261
+ nil)
262
+ ~'ret)
263
+ call-with-resolved))
264
+
265
+ ;; Case: resolve db, return db-id (as-of, since, history, db-with)
266
+ (and resolves-db? (= returns :db-id))
267
+ (let [call-expr (if variadic?
268
+ `(apply ~api-impl ~'origin-db ~@rest-named-args ~'rest-args)
269
+ `(~api-impl ~'origin-db ~@rest-named-args))]
270
+ `(let [~'origin-db (~'resolve-db ~first-arg)
271
+ ~'parent-conn-id (get-in @~'dbs [~first-arg :conn-id])
272
+ ~result-sym ~call-expr
273
+ ~db-id-sym (~'generate-db-id ~result-sym)]
274
+ (swap! ~'dbs assoc ~db-id-sym {:db ~result-sym :conn-id ~'parent-conn-id})
275
+ ~db-id-sym))
276
+
277
+ ;; Case: resolve db, return value (pull, entity, datoms, etc.)
278
+ (and resolves-db? (= returns :value))
279
+ (let [call-expr (if variadic?
280
+ `(apply ~api-impl ~'db ~@rest-named-args ~'rest-args)
281
+ `(~api-impl ~'db ~@rest-named-args))]
282
+ `(let [~'db (~'resolve-db ~first-arg)
283
+ ~result-sym ~call-expr]
284
+ ~(wrap-with-transform transform result-sym)))
285
+
286
+ ;; Case: no resolution, return conn-id
287
+ (= returns :conn-id)
288
+ `(let [~result-sym ~(generate-api-call api-impl named-args variadic?)
289
+ ~conn-id-sym (~'generate-conn-id ~(or first-arg {}))]
290
+ (swap! ~'conns assoc ~conn-id-sym ~result-sym)
291
+ ~conn-id-sym)
292
+
293
+ ;; Case: no resolution, return value (database-exists?, create-database, etc.)
294
+ :else
295
+ `(let [~result-sym ~(generate-api-call api-impl named-args variadic?)]
296
+ ~(wrap-with-transform transform result-sym)))))
297
+
298
+ (defn generate-function
299
+ "Generate a complete pod function definition."
300
+ [op-name]
301
+ (let [spec (get api-specification op-name)
302
+ overlay (get pod-operations op-name)
303
+ custom-fn (:custom-fn overlay)]
304
+
305
+ (if custom-fn
306
+ ;; Use custom function body from overlay
307
+ `(def ~op-name ~custom-fn)
308
+
309
+ ;; Generate function from specification
310
+ (let [arities (extract-arities (:args spec))
311
+ api-impl (symbol "d" (name op-name))]
312
+ (if (= 1 (count arities))
313
+ ;; Single arity
314
+ (let [arity-info (arity-arg-names (first arities))]
315
+ `(defn ~op-name ~(:args arity-info)
316
+ ~(generate-arity-body op-name overlay arity-info api-impl)))
317
+
318
+ ;; Multiple arities
319
+ `(defn ~op-name
320
+ ~@(for [arity arities
321
+ :let [arity-info (arity-arg-names arity)]]
322
+ `(~(:args arity-info)
323
+ ~(generate-arity-body op-name overlay arity-info api-impl)))))))))
324
+
325
+ ;; =============================================================================
326
+ ;; Describe Map Generation
327
+ ;; =============================================================================
328
+
329
+ (defn first-sentence
330
+ "Extract first sentence from docstring."
331
+ [doc]
332
+ (when doc
333
+ (-> doc
334
+ (str/split #"\.\s")
335
+ first
336
+ (str "."))))
337
+
338
+ (defn generate-var-entry
339
+ "Generate a describe-map entry for an operation."
340
+ [op-name]
341
+ {"name" (name op-name)})
342
+
343
+ (defn generate-describe-map
344
+ "Generate the complete describe-map for pod protocol."
345
+ []
346
+ (let [;; Operations from overlay
347
+ op-entries (for [op-name (keys pod-operations)]
348
+ (generate-var-entry op-name))
349
+ ;; Pod additions
350
+ addition-entries (for [[op-name config] pod-additions]
351
+ {"name" (name op-name)})
352
+ ;; with-db macro
353
+ macro-entry {"name" "with-db" "code" with-db-macro-code}]
354
+ (vec (concat op-entries addition-entries [macro-entry]))))
355
+
356
+ ;; =============================================================================
357
+ ;; Publics Map Generation
358
+ ;; =============================================================================
359
+
360
+ (defn generate-publics-map
361
+ "Generate the publics lookup map."
362
+ []
363
+ (let [ops (for [op-name (keys pod-operations)]
364
+ [(list 'quote op-name) op-name])
365
+ additions (for [[op-name _] pod-additions]
366
+ [(list 'quote op-name) op-name])]
367
+ `(def ~'publics
368
+ ~(into {} (concat ops additions)))))
369
+
370
+ ;; =============================================================================
371
+ ;; Full Code Generation
372
+ ;; =============================================================================
373
+
374
+ (defn generate-runtime-code
375
+ "Generate the pod runtime infrastructure code."
376
+ []
377
+ '((def conns (atom {}))
378
+ (def dbs (atom {}))
379
+
380
+ (defn generate-conn-id [config]
381
+ (str "conn:" (hash config)))
382
+
383
+ (defn generate-db-id
384
+ "Generate a unique ID for a database snapshot.
385
+ Always creates a fresh ID to ensure speculative dbs (from db-with)
386
+ don't collide with their source dbs."
387
+ [db]
388
+ (str (datahike.writing/create-commit-id db)))
389
+
390
+ (defn resolve-conn [conn-id]
391
+ (or (get @conns conn-id)
392
+ (throw (ex-info "Connection not found" {:conn-id conn-id}))))
393
+
394
+ (defn resolve-db [db-id]
395
+ (or (get-in @dbs [db-id :db])
396
+ (throw (ex-info "Database not found" {:db-id db-id}))))
397
+
398
+ (defn release-db [db-id]
399
+ (swap! dbs dissoc db-id)
400
+ {})))
401
+
402
+ (defn generate-all-functions
403
+ "Generate all pod function definitions."
404
+ []
405
+ (for [op-name (keys pod-operations)]
406
+ (generate-function op-name)))
407
+
408
+ (defn generate-pod-namespace
409
+ "Generate the complete pod namespace code."
410
+ []
411
+ (let [runtime (generate-runtime-code)
412
+ functions (generate-all-functions)
413
+ publics (generate-publics-map)
414
+ describe-map `(def ~'describe-map ~(generate-describe-map))]
415
+ {:runtime runtime
416
+ :functions functions
417
+ :publics publics
418
+ :describe-map describe-map}))
419
+
420
+ ;; =============================================================================
421
+ ;; Validation
422
+ ;; =============================================================================
423
+
424
+ (defn validate-coverage
425
+ "Validate that all API operations are either implemented or excluded."
426
+ []
427
+ (let [all-ops (set (keys api-specification))
428
+ implemented (set (keys pod-operations))
429
+ excluded (set (keys pod-excluded-operations))
430
+ covered (set/union implemented excluded)
431
+ missing (set/difference all-ops covered)]
432
+ (when (seq missing)
433
+ (println "WARNING: Operations missing from pod overlay:")
434
+ (doseq [op missing]
435
+ (println " -" op)))
436
+ (empty? missing)))
437
+
438
+ ;; =============================================================================
439
+ ;; Debug / Development
440
+ ;; =============================================================================
441
+
442
+ (defn print-generated-function
443
+ "Print generated code for a single function (for debugging)."
444
+ [op-name]
445
+ (clojure.pprint/pprint (generate-function op-name)))
446
+
447
+ (defn print-all-generated
448
+ "Print all generated code (for debugging)."
449
+ []
450
+ (let [{:keys [runtime functions publics describe-map]} (generate-pod-namespace)]
451
+ (println ";; Runtime")
452
+ (doseq [form runtime]
453
+ (clojure.pprint/pprint form)
454
+ (println))
455
+ (println "\n;; Functions")
456
+ (doseq [form functions]
457
+ (clojure.pprint/pprint form)
458
+ (println))
459
+ (println "\n;; Publics")
460
+ (clojure.pprint/pprint publics)
461
+ (println "\n;; Describe Map")
462
+ (clojure.pprint/pprint describe-map)))
463
+
464
+ ;; =============================================================================
465
+ ;; Compile-Time Code Generation Macros
466
+ ;; =============================================================================
467
+
468
+ (defmacro defpod-runtime
469
+ "Generate pod runtime infrastructure (atoms, helper functions)."
470
+ []
471
+ `(do
472
+ ~@(generate-runtime-code)))
473
+
474
+ (defmacro defpod-functions
475
+ "Generate all pod API functions from the overlay specification."
476
+ []
477
+ `(do
478
+ ~@(generate-all-functions)))
479
+
480
+ (defmacro defpod-publics
481
+ "Generate the publics map for pod protocol lookup."
482
+ []
483
+ (generate-publics-map))
484
+
485
+ (defmacro defpod-describe-map
486
+ "Generate the describe-map for pod protocol."
487
+ []
488
+ `(def ~'describe-map ~(generate-describe-map)))