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,172 @@
1
+ (ns datahike.js.api
2
+ "JavaScript API for Datahike with Promise conversion and data transformation"
3
+ (:refer-clojure :exclude [filter])
4
+ (:require [datahike.api.specification :refer [api-specification]]
5
+ [datahike.api.impl]
6
+ [datahike.store] ;; Register :mem backend
7
+ [datahike.db.interface]
8
+ [datahike.datom]
9
+ [cljs.core.async :refer [<!]]
10
+ [clojure.string :as str]
11
+ [clojure.walk :as walk]
12
+ [goog.object :as gobj])
13
+ (:require-macros [cljs.core.async.macros :refer [go]]
14
+ [datahike.js.api-macros :refer [emit-js-api]]))
15
+
16
+ ;; Register Node.js file backend - conditional require
17
+ ;; For Node.js: konserve.node-filestore is added to shadow-cljs :entries
18
+ ;; For browser: module is excluded from build
19
+ (when (and (exists? js/require)
20
+ (fn? js/require))
21
+ (try
22
+ (js/require "./konserve.node_filestore")
23
+ (catch :default _ nil)))
24
+
25
+ ;; =============================================================================
26
+ ;; Data Conversion Helpers
27
+ ;;
28
+ ;; Universal EDN Conversion Rules (consistent across Python, JavaScript, Java):
29
+ ;; - Keys: always keywordized
30
+ ;; - Values: ":" prefix = keyword, else literal
31
+ ;; - Escape: "\\:" for literal colon strings
32
+ ;; - Bonus: UUID auto-detection for convenience
33
+ ;; =============================================================================
34
+
35
+ (def ^:private uuid-regex
36
+ "Regex pattern for UUID strings (8-4-4-4-12 hex digits)"
37
+ #"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$")
38
+
39
+ (defn- uuid-string?
40
+ "Check if a string is a valid UUID format"
41
+ [s]
42
+ (and (string? s) (re-matches uuid-regex s)))
43
+
44
+ (defn- convert-string
45
+ "Convert a string to appropriate Clojure type following universal EDN rules:
46
+ - '\\:literal' -> ':literal' (escaped colon becomes literal string with :)
47
+ - ':keyword' -> keyword
48
+ - UUID format -> UUID object (convenience)
49
+ - otherwise -> string"
50
+ [s]
51
+ (cond
52
+ ;; Escaped colon - strip backslash and return literal string
53
+ (str/starts-with? s "\\:") (subs s 1)
54
+ ;; Colon prefix - convert to keyword
55
+ (str/starts-with? s ":") (keyword (subs s 1))
56
+ ;; UUID auto-detection (convenience feature)
57
+ (uuid-string? s) (uuid s)
58
+ ;; Regular string
59
+ :else s))
60
+
61
+ (defn js->clj-recursive
62
+ "Recursively convert JS objects to Clojure data with keyword keys.
63
+ Also converts strings like ':keyword' to keywords and UUID strings to UUIDs."
64
+ [x]
65
+ (cond
66
+ ;; Check for JS object first (but not arrays, functions, or null)
67
+ (and (object? x)
68
+ (not (array? x))
69
+ (not (fn? x))
70
+ (not (nil? x)))
71
+ (into {} (for [k (js-keys x)]
72
+ (let [v (gobj/get x k)
73
+ ;; Convert keys to keywords, stripping leading : if present
74
+ k-kw (if (str/starts-with? k ":")
75
+ (keyword (subs k 1))
76
+ (keyword k))]
77
+ [k-kw (js->clj-recursive v)])))
78
+
79
+ ;; Arrays become vectors
80
+ (array? x)
81
+ (mapv js->clj-recursive x)
82
+
83
+ ;; Strings: convert keywords, UUIDs, or pass through
84
+ (string? x)
85
+ (convert-string x)
86
+
87
+ ;; Everything else passes through
88
+ :else x))
89
+
90
+ (defn clj->js-recursive
91
+ "Recursively convert Clojure data to JS objects.
92
+ Converts keywords to strings with ':' prefix.
93
+ Datahike objects (DB, connections, datoms) pass through unchanged."
94
+ [x]
95
+ (cond
96
+ ;; Datahike DB objects - pass through unchanged
97
+ (satisfies? datahike.db.interface/IDB x)
98
+ x
99
+
100
+ ;; Datoms - pass through unchanged
101
+ (= (type x) datahike.datom.Datom)
102
+ x
103
+
104
+ ;; Connections (check for typical connection keys)
105
+ (and (map? x) (:conn-atom x))
106
+ x
107
+
108
+ ;; Keywords become strings with ":"
109
+ (keyword? x)
110
+ (str x)
111
+
112
+ ;; Maps become JS objects
113
+ (map? x)
114
+ (let [obj (js-obj)]
115
+ (doseq [[k v] x]
116
+ (gobj/set obj
117
+ (if (keyword? k) (name k) (str k))
118
+ (clj->js-recursive v)))
119
+ obj)
120
+
121
+ ;; Sequential collections become arrays
122
+ (sequential? x)
123
+ (into-array (map clj->js-recursive x))
124
+
125
+ ;; Sets become arrays
126
+ (set? x)
127
+ (into-array (map clj->js-recursive x))
128
+
129
+ ;; Everything else passes through
130
+ :else x))
131
+
132
+ ;; =============================================================================
133
+ ;; Async/Promise Conversion
134
+ ;; =============================================================================
135
+
136
+ (defn maybe-chan->promise
137
+ "Convert a core.async channel to a Promise, or return value directly if not a channel.
138
+ This handles the dynamic async/sync execution in Datahike API.
139
+
140
+ Errors returned on the channel (not thrown) are properly rejected by checking
141
+ if the result is a js/Error or ExceptionInfo."
142
+ [x]
143
+ (if (satisfies? cljs.core.async.impl.protocols/Channel x)
144
+ (js/Promise.
145
+ (fn [resolve reject]
146
+ (go
147
+ (try
148
+ (let [result (<! x)]
149
+ ;; Check if result is an error object - reject promise if so
150
+ (if (or (instance? js/Error result)
151
+ (instance? ExceptionInfo result))
152
+ (reject result)
153
+ (resolve result)))
154
+ (catch :default e
155
+ ;; Catch any exceptions thrown during channel operations
156
+ (reject e))))))
157
+ (js/Promise.resolve x)))
158
+
159
+ ;; =============================================================================
160
+ ;; Generate All API Functions
161
+ ;; =============================================================================
162
+
163
+ (emit-js-api)
164
+
165
+ ;; =============================================================================
166
+ ;; Additional JS-specific Helpers
167
+ ;; =============================================================================
168
+
169
+ (defn ^:export isPromise
170
+ "Check if a value is a Promise."
171
+ [x]
172
+ (instance? js/Promise x))
@@ -0,0 +1,22 @@
1
+ (ns datahike.js.api-macros
2
+ "Macros for generating JavaScript API."
3
+ (:require [datahike.api.specification :refer [api-specification]]
4
+ [datahike.codegen.naming :refer [js-skip-list clj-name->js-name]]))
5
+
6
+ (defmacro emit-js-api
7
+ "Generate JavaScript API functions from api-specification.
8
+ This macro must be in a .clj file since it's used by ClojureScript."
9
+ []
10
+ `(do
11
+ ~@(for [[clj-fn-name {:keys [doc impl]}] api-specification
12
+ :when (not (contains? js-skip-list clj-fn-name))
13
+ :let [js-fn-name (symbol (clj-name->js-name clj-fn-name))
14
+ impl-fn (symbol (namespace impl) (name impl))]]
15
+ `(defn ~(with-meta js-fn-name {:export true :doc doc})
16
+ [& args#]
17
+ (let [clj-args# (map datahike.js.api/js->clj-recursive args#)
18
+ result# (apply ~impl-fn clj-args#)]
19
+ (-> result#
20
+ datahike.js.api/maybe-chan->promise
21
+ (.then datahike.js.api/clj->js-recursive)))))))
22
+
@@ -0,0 +1,163 @@
1
+ (ns ^:no-doc datahike.js
2
+ (:refer-clojure :exclude [filter])
3
+ (:require
4
+ [goog.object :as go]
5
+ [datahike.core :as d]
6
+ [clojure.walk :as walk]
7
+ [cljs.reader]))
8
+
9
+ ;; Conversions
10
+
11
+ (defn- keywordize [s]
12
+ (if (and (string? s) (= (subs s 0 1) ":"))
13
+ (keyword (subs s 1))
14
+ s))
15
+
16
+ (defn- schema->clj [schema]
17
+ (->> (js->clj schema)
18
+ (reduce-kv
19
+ (fn [m k v] (assoc m k (walk/postwalk keywordize v))) {})))
20
+
21
+ (declare entities->clj)
22
+
23
+ (defn- entity-map->clj [e]
24
+ (walk/postwalk
25
+ (fn [form]
26
+ (if (and (map? form) (contains? form ":db/id"))
27
+ (-> form
28
+ (dissoc ":db/id")
29
+ (assoc :db/id (get form ":db/id")))
30
+ form))
31
+ e))
32
+
33
+ (defn- entity->clj [e]
34
+ (cond
35
+ (map? e)
36
+ (entity-map->clj e)
37
+
38
+ (= (first e) ":db.fn/call")
39
+ (let [[_ f & args] e]
40
+ (concat [:db.fn/call (fn [& args] (entities->clj (apply f args)))] args))
41
+
42
+ (sequential? e)
43
+ (let [[op & entity] e]
44
+ (concat [(keywordize op)] entity))))
45
+
46
+ (defn- entities->clj [entities]
47
+ (->> (js->clj entities)
48
+ (map entity->clj)))
49
+
50
+ (defn- tempids->js [tempids]
51
+ (let [obj (js-obj)]
52
+ (doseq [[k v] tempids]
53
+ (go/set obj (str k) v))
54
+ obj))
55
+
56
+ (defn- tx-report->js [report]
57
+ #js {:db_before (:db-before report)
58
+ :db_after (:db-after report)
59
+ :tx_data (->> (:tx-data report) into-array)
60
+ :tempids (tempids->js (:tempids report))
61
+ :tx_meta (:tx-meta report)})
62
+
63
+ (defn js->Datom [d]
64
+ (if (array? d)
65
+ (d/datom (aget d 0) (aget d 1) (aget d 2) (or (aget d 3) d/tx0) (or (aget d 4) true))
66
+ (d/datom (.-e d) (.-a d) (.-v d) (or (.-tx d) d/tx0) (or (.-added d) true))))
67
+
68
+ (defn- pull-result->js
69
+ [result]
70
+ (->> result
71
+ (walk/postwalk #(if (keyword? %) (str %) %))
72
+ clj->js))
73
+
74
+ ;; Public API
75
+
76
+ (defn ^:export empty_db [& [schema]]
77
+ (d/empty-db (schema->clj schema)))
78
+
79
+ (defn ^:export init_db [datoms & [schema]]
80
+ (d/init-db (map js->Datom datoms) (schema->clj schema)))
81
+
82
+ (defn ^:export q [query & sources]
83
+ (let [query (cljs.reader/read-string query)
84
+ results (apply d/q query sources)]
85
+ (clj->js results)))
86
+
87
+ (defn ^:export pull [db pattern eid]
88
+ (let [pattern (cljs.reader/read-string pattern)
89
+ eid (js->clj eid)
90
+ results (d/pull db pattern eid)]
91
+ (pull-result->js results)))
92
+
93
+ (defn ^:export pull_many [db pattern eids]
94
+ (let [pattern (cljs.reader/read-string pattern)
95
+ eids (js->clj eids)
96
+ results (d/pull-many db pattern eids)]
97
+ (pull-result->js results)))
98
+
99
+ (defn ^:export db_with [db entities]
100
+ (d/db-with db (entities->clj entities)))
101
+
102
+ (defn ^:export entity [db eid]
103
+ (d/entity db (js->clj eid)))
104
+
105
+ (def ^:export touch d/touch)
106
+ (def ^:export entity_db d/entity-db)
107
+ (def ^:export filter d/filter)
108
+ (def ^:export is_filtered d/is-filtered)
109
+
110
+ (defn ^:export create_conn [& [schema]]
111
+ (d/create-conn (schema->clj schema)))
112
+
113
+ (def ^:export conn_from_db d/conn-from-db)
114
+
115
+ (defn ^:export conn_from_datoms
116
+ ([datoms] (conn_from_db (init_db datoms)))
117
+ ([datoms schema] (conn_from_db (init_db datoms schema))))
118
+
119
+ (defn ^:export db [conn] @conn)
120
+
121
+ (defn ^:export transact [conn entities & [tx-meta]]
122
+ (let [entities (entities->clj entities)
123
+ report (-> (d/-transact! conn entities tx-meta)
124
+ tx-report->js)]
125
+ (doseq [[_ callback] @(:listeners (meta conn))]
126
+ (callback report))
127
+ report))
128
+
129
+ (defn ^:export reset_conn [conn db & [tx-meta]]
130
+ (let [report #js {:db_before @conn
131
+ :db_after db
132
+ :tx_data (into-array
133
+ (concat
134
+ (map #(assoc % :added false) (d/datoms @conn :eavt))
135
+ (d/datoms db :eavt)))
136
+ :tx_meta tx-meta}]
137
+ (reset! conn db)
138
+ (doseq [[_ callback] @(:listeners (meta conn))]
139
+ (callback report))
140
+ db))
141
+
142
+ (def ^:export listen d/listen!)
143
+ (def ^:export unlisten d/unlisten!)
144
+
145
+ (defn ^:export resolve_tempid [tempids tempid]
146
+ (go/get tempids (str tempid)))
147
+
148
+ (defn ^:export datoms [db index & components]
149
+ (->> (apply d/datoms db (keywordize index) components)
150
+ into-array))
151
+
152
+ (defn ^:export seek_datoms [db index & components]
153
+ (->> (apply d/seek-datoms db (keywordize index) components)
154
+ into-array))
155
+
156
+ (defn ^:export index_range [db attr start end]
157
+ (into-array (d/index-range db attr start end)))
158
+
159
+ (defn ^:export squuid []
160
+ (str (d/squuid)))
161
+
162
+ (defn ^:export squuid_time_millis [uuid]
163
+ (d/squuid-time-millis (cljs.core/uuid uuid)))
@@ -0,0 +1,209 @@
1
+ (ns datahike.json
2
+ "JSON related translations."
3
+ (:require [clojure.edn :as edn]
4
+ [clojure.walk :as walk]
5
+ [datahike.store :refer [store-identity]]
6
+ [datahike.readers :as readers]
7
+ [datahike.connector]
8
+ [datahike.datom :as dd]
9
+ [datahike.schema :as s]
10
+ [jsonista.core :as j]
11
+ [jsonista.tagged :as jt])
12
+ #?(:clj
13
+ (:import [datahike.datom Datom]
14
+ [datahike.impl.entity Entity]
15
+ [com.fasterxml.jackson.core JsonGenerator]
16
+ [datahike.db HistoricalDB AsOfDB SinceDB])))
17
+
18
+ (defn config->store-id [config]
19
+ [(store-identity (:store config))
20
+ (:branch config)])
21
+
22
+ (defn db->map [db]
23
+ (let [{:keys [config meta max-eid max-tx]} db]
24
+ {:store-id (config->store-id config)
25
+ :commit-id (:datahike/commit-id meta)
26
+ :max-eid max-eid
27
+ :max-tx max-tx}))
28
+
29
+ (declare mapper)
30
+
31
+ (defn write-to-generator [f]
32
+ (fn [x ^JsonGenerator gen]
33
+ (.writeRawValue gen (j/write-value-as-string (f x) mapper))))
34
+
35
+ (def json-base-handlers
36
+ {clojure.lang.Keyword
37
+ {:tag "!kw"
38
+ :encode jt/encode-keyword
39
+ :decode keyword}
40
+
41
+ clojure.lang.Symbol
42
+ {:tag "!sym"
43
+ :encode jt/encode-str
44
+ :decode symbol}
45
+
46
+ clojure.lang.PersistentHashSet
47
+ {:tag "!set"
48
+ :encode jt/encode-collection
49
+ :decode set}
50
+
51
+ java.util.UUID
52
+ {:tag "!uuid"
53
+ :encode jt/encode-str
54
+ :decode #(java.util.UUID/fromString %)}
55
+
56
+ java.util.Date
57
+ {:tag "!date"
58
+ :encode #(jt/encode-str (.getTime ^java.util.Date %1) %2)
59
+ :decode #(java.util.Date. (Long/parseLong %))}})
60
+
61
+ (def mapper-opts
62
+ {:encode-key-fn true
63
+ :decode-key-fn true
64
+ :modules [(jt/module
65
+ {:handlers
66
+ (merge
67
+ json-base-handlers
68
+ {datahike.connector.Connection
69
+ {:tag "!datahike/Connection"
70
+ :encode (write-to-generator #(config->store-id (:config @(:wrapped-atom %))))
71
+ :decode datahike.readers/connection-from-reader}
72
+
73
+ datahike.datom.Datom
74
+ {:tag "!datahike/Datom"
75
+ :encode (write-to-generator
76
+ (fn [^Datom d]
77
+ [(.-e d) (.-a d) (.-v d) (dd/datom-tx d) (dd/datom-added d)]))
78
+ :decode datahike.readers/datom-from-reader}
79
+
80
+ datahike.db.TxReport
81
+ {:tag "!datahike/TxReport"
82
+ :encode (write-to-generator #(into {} %))
83
+ :decode datahike.db/map->TxReport}
84
+
85
+ datahike.db.DB
86
+ {:tag "!datahike/DB"
87
+ :encode (write-to-generator db->map)
88
+ :decode datahike.readers/db-from-reader}
89
+
90
+ datahike.db.HistoricalDB
91
+ {:tag "!datahike/HistoricalDB"
92
+ :encode (write-to-generator
93
+ (fn [{:keys [origin-db]}]
94
+ {:origin origin-db}))
95
+ :decode datahike.readers/history-from-reader}
96
+
97
+ datahike.db.SinceDB
98
+ {:tag "!datahike/SinceDB"
99
+ :encode (write-to-generator
100
+ (fn [{:keys [origin-db time-point]}]
101
+ {:origin origin-db
102
+ :time-point time-point}))
103
+ :decode datahike.readers/since-from-reader}
104
+
105
+ datahike.db.AsOfDB
106
+ {:tag "!datahike/AsOfDB"
107
+ :encode (write-to-generator
108
+ (fn [{:keys [origin-db time-point]}]
109
+ {:origin origin-db
110
+ :time-point time-point}))
111
+ :decode datahike.readers/as-of-from-reader}
112
+
113
+ datahike.impl.entity.Entity
114
+ {:tag "!datahike/Entity"
115
+ :encode (write-to-generator
116
+ (fn [^Entity e]
117
+ (assoc (into {} e)
118
+ :db (.-db e)
119
+ :eid (.-eid e))))
120
+ :decode datahike.readers/entity-from-reader}})})]})
121
+
122
+ (def mapper (j/object-mapper mapper-opts))
123
+
124
+ ;; import from datahike-server for JSON transact support with this code you can
125
+ ;; pass normal JSON maps or arrays of Datoms and strings will be automatically
126
+ ;; converted to keywords depending on their position
127
+
128
+ (def number-re #"\d+(\.\d+)?")
129
+ (def number-format-instance (java.text.NumberFormat/getInstance))
130
+
131
+ (defn- filter-value-type-attrs [valtypes schema]
132
+ (into #{} (filter #(-> % schema :db/valueType valtypes) (keys schema))))
133
+
134
+ (def ^:private filter-kw-attrs
135
+ (partial filter-value-type-attrs #{:db.type/keyword :db.type/value :db.type/cardinality :db.type/unique}))
136
+
137
+ (def keyword-valued-schema-attrs (filter-kw-attrs s/implicit-schema-spec))
138
+
139
+ (defn- xf-val [f v]
140
+ (if (vector? v) (map f v) (f v)))
141
+
142
+ (declare handle-id-or-av-pair)
143
+
144
+ (defn- xf-ref-val [v valtype-attrs-map db]
145
+ (if (vector? v)
146
+ (walk/prewalk #(handle-id-or-av-pair % valtype-attrs-map db) v)
147
+ v))
148
+
149
+ (defn keywordize-string [s]
150
+ (if (string? s) (keyword s) s))
151
+
152
+ (defn ident-for [^datahike.db.DB db a]
153
+ (if (and (number? a) (some? db)) (.-ident-for db a) a))
154
+
155
+ (defn cond-xf-val
156
+ [a-ident v {:keys [ref-attrs long-attrs keyword-attrs symbol-attrs] :as valtype-attrs-map} db]
157
+ (cond
158
+ (contains? ref-attrs a-ident) (xf-ref-val v valtype-attrs-map db)
159
+ (contains? long-attrs a-ident) (xf-val long v)
160
+ (contains? keyword-attrs a-ident) (xf-val keyword v)
161
+ (contains? symbol-attrs a-ident) (xf-val symbol v)
162
+ :else v))
163
+
164
+ (defn handle-id-or-av-pair
165
+ ([v valtype-attrs-map]
166
+ (handle-id-or-av-pair v valtype-attrs-map nil))
167
+ ([v valtype-attrs-map db]
168
+ (if (and (vector? v) (= (count v) 2))
169
+ (let [a (keywordize-string (first v))]
170
+ [a (cond-xf-val (ident-for db a) (nth v 1) valtype-attrs-map db)])
171
+ v)))
172
+
173
+ (defn- xf-tx-data-map [m valtype-attrs-map db]
174
+ (into {}
175
+ (map (fn [[a v]]
176
+ [a (if (= :db/id a)
177
+ (handle-id-or-av-pair [a v] valtype-attrs-map db)
178
+ (cond-xf-val a v valtype-attrs-map db))])
179
+ m)))
180
+
181
+ (defn- xf-tx-data-vec [tx-vec valtype-attrs-map db]
182
+ (let [op (first tx-vec)
183
+ [e a v] (rest tx-vec)
184
+ a (keywordize-string a)]
185
+ (vec (filter some? (list (keyword op)
186
+ (handle-id-or-av-pair e valtype-attrs-map db)
187
+ a
188
+ (cond-xf-val (ident-for db a) v valtype-attrs-map db))))))
189
+
190
+ (defn get-valtype-attrs-map [schema]
191
+ (let [ref-valued-attrs (filter-value-type-attrs #{:db.type/ref} schema)
192
+ long-valued-attrs (filter-value-type-attrs #{:db.type/long} schema)
193
+ kw-valued-attrs (clojure.set/union keyword-valued-schema-attrs (filter-kw-attrs schema))
194
+ sym-valued-attrs (filter-value-type-attrs #{:db.type/symbol} schema)]
195
+ {:ref-attrs ref-valued-attrs
196
+ :long-attrs long-valued-attrs
197
+ :keyword-attrs kw-valued-attrs
198
+ :symbol-attrs sym-valued-attrs}))
199
+
200
+ (defn xf-data-for-tx [tx-data db]
201
+ (let [valtype-attrs-map (get-valtype-attrs-map (:schema db))]
202
+ (map #(let [xf-fn (cond (map? %) xf-tx-data-map
203
+ (vector? %) xf-tx-data-vec
204
+ ; Q: Is this error appropriate?
205
+ :else (throw (ex-info "Only maps and vectors allowed in :tx-data and :tx-meta"
206
+ {:event :handlers/transact :data tx-data})))]
207
+ (xf-fn % valtype-attrs-map db))
208
+ tx-data)))
209
+