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,383 @@
1
+ # EDN Conversion Rules for Language Bindings
2
+
3
+ This document describes the universal rules for converting native data structures (Python dicts, JavaScript objects, Java Maps) to EDN (Extensible Data Notation) across all Datahike language bindings.
4
+
5
+ ## Design Goal
6
+
7
+ Provide a **consistent, explicit, and predictable** mapping between native language data structures and EDN that works the same way across Python, JavaScript, and Java bindings.
8
+
9
+ ## The Universal Rule
10
+
11
+ > **Keys are always keywordized. Values starting with `:` become keywords, everything else remains literal.**
12
+
13
+ ### Key Conversion
14
+
15
+ All map/dict/object keys are automatically converted to EDN keywords:
16
+
17
+ | Input | EDN Output | Notes |
18
+ |-------|------------|-------|
19
+ | `"name"` | `:name` | Simple key |
20
+ | `"person/name"` | `:person/name` | Namespaced key |
21
+ | `":db/id"` | `:db/id` | Leading `:` is stripped (convenience) |
22
+ | `"keep-history?"` | `:keep-history?` | Question mark preserved |
23
+
24
+ ### Value Conversion
25
+
26
+ Values are converted based on their type and content:
27
+
28
+ | Input Type | Input Value | EDN Output | Rule |
29
+ |------------|-------------|------------|------|
30
+ | String starting with `:` | `":active"` | `:active` | Keyword |
31
+ | String without `:` | `"Alice"` | `"Alice"` | String |
32
+ | String with `\:` prefix | `"\\:literal"` | `":literal"` | Escaped literal |
33
+ | Integer | `42` | `42` | Number |
34
+ | Float | `3.14` | `3.14` | Number |
35
+ | Boolean | `true` / `false` | `true` / `false` | Boolean |
36
+ | Null/None | `null` / `None` | `nil` | Nil |
37
+ | List/Array | `[1, 2, 3]` | `[1 2 3]` | Vector |
38
+ | Nested Map/Dict | `{"a": 1}` | `{:a 1}` | Recursive |
39
+
40
+ ## Examples by Language
41
+
42
+ ### Python
43
+
44
+ ```python
45
+ # Configuration
46
+ import uuid
47
+
48
+ config = {
49
+ "store": {
50
+ "backend": ":memory", # Keyword
51
+ "id": str(uuid.uuid4()) # String (memory backend requires UUID)
52
+ },
53
+ "schema-flexibility": ":read", # Keyword
54
+ "keep-history?": True # Boolean
55
+ }
56
+ # → {:store {:backend :memory :id "<uuid-string>"}
57
+ # :schema-flexibility :read
58
+ # :keep-history? true}
59
+
60
+ # Transaction data
61
+ data = [
62
+ {"name": "Alice", "status": ":active"},
63
+ {"name": "Bob", "status": ":inactive"}
64
+ ]
65
+ # → [{:name "Alice" :status :active}
66
+ # {:name "Bob" :status :inactive}]
67
+
68
+ # Schema transaction
69
+ schema = [
70
+ {
71
+ "db/ident": ":person/name",
72
+ "db/valueType": ":db.type/string",
73
+ "db/cardinality": ":db.cardinality/one"
74
+ }
75
+ ]
76
+ # → [{:db/ident :person/name
77
+ # :db/valueType :db.type/string
78
+ # :db/cardinality :db.cardinality/one}]
79
+ ```
80
+
81
+ ### JavaScript
82
+
83
+ ```javascript
84
+ // Configuration
85
+ const crypto = require('crypto');
86
+ const config = {
87
+ store: {
88
+ backend: ':memory', // Keyword
89
+ id: crypto.randomUUID() // String (memory backend requires UUID)
90
+ },
91
+ 'schema-flexibility': ':read', // Keyword
92
+ 'keep-history?': true // Boolean
93
+ };
94
+ // → {:store {:backend :memory :id "<uuid-string>"}
95
+ // :schema-flexibility :read
96
+ // :keep-history? true}
97
+
98
+ // Transaction data
99
+ const data = [
100
+ {name: 'Alice', status: ':active'},
101
+ {name: 'Bob', status: ':inactive'}
102
+ ];
103
+ // → [{:name "Alice" :status :active}
104
+ // {:name "Bob" :status :inactive}]
105
+ ```
106
+
107
+ ### Java
108
+
109
+ ```java
110
+ // Configuration
111
+ import java.util.UUID;
112
+
113
+ Map<String, Object> config = Map.of(
114
+ "store", Map.of(
115
+ "backend", ":memory", // Keyword
116
+ "id", UUID.randomUUID().toString() // String (memory backend requires UUID)
117
+ ),
118
+ "schema-flexibility", ":read" // Keyword
119
+ );
120
+ // → {:store {:backend :memory :id "<uuid-string>"}
121
+ // :schema-flexibility :read}
122
+
123
+ // Or use builders (convenience)
124
+ // Note: memory backend requires UUID identifier
125
+ Database db = Database.memory(UUID.randomUUID().toString())
126
+ .schemaFlexibility(SchemaFlexibility.READ)
127
+ .build();
128
+ ```
129
+
130
+ ## Edge Cases and Escape Hatches
131
+
132
+ ### Literal Colon Strings
133
+
134
+ To include a string that starts with `:` without it becoming a keyword, use backslash escape:
135
+
136
+ ```python
137
+ # Python
138
+ {"literal": "\\:starts-with-colon"}
139
+ # → {:literal ":starts-with-colon"}
140
+
141
+ # JavaScript
142
+ {literal: '\\:starts-with-colon'}
143
+ # → {:literal ":starts-with-colon"}
144
+
145
+ # Java
146
+ Map.of("literal", "\\:starts-with-colon")
147
+ // → {:literal ":starts-with-colon"}
148
+ ```
149
+
150
+ **Note:** In Python/Java string literals, you need to escape the backslash itself: `"\\:"` → literal `\:` → EDN `":value"`
151
+
152
+ ### Helper Modules for Complex Types
153
+
154
+ All languages provide helper modules for explicit type control:
155
+
156
+ ```python
157
+ # Python
158
+ from datahike import edn
159
+
160
+ {
161
+ "uuid": edn.uuid("550e8400-e29b-41d4-a716-446655440000"),
162
+ "inst": edn.inst("2024-01-01T00:00:00Z"),
163
+ "literal": edn.string(":force-string"),
164
+ "keyword": edn.keyword("name", "person") # :person/name
165
+ }
166
+ ```
167
+
168
+ ```javascript
169
+ // JavaScript
170
+ import {edn} from 'datahike';
171
+
172
+ {
173
+ uuid: edn.uuid('550e8400-e29b-41d4-a716-446655440000'),
174
+ inst: edn.inst('2024-01-01T00:00:00Z'),
175
+ literal: edn.string(':force-string'),
176
+ keyword: edn.keyword('person', 'name') // :person/name
177
+ }
178
+ ```
179
+
180
+ ```java
181
+ // Java
182
+ import static datahike.edn.EDN.*;
183
+
184
+ Map.of(
185
+ "uuid", uuid("550e8400-e29b-41d4-a716-446655440000"),
186
+ "inst", inst("2024-01-01T00:00:00Z"),
187
+ "literal", string(":force-string"),
188
+ "keyword", keyword("person", "name") // :person/name
189
+ )
190
+ ```
191
+
192
+ ### EDN String Escape Hatch
193
+
194
+ For truly complex cases (database functions, unusual syntax), all bindings accept raw EDN strings:
195
+
196
+ ```python
197
+ # Python
198
+ db.transact('''
199
+ [{:db/id #db/id[:db.part/user]
200
+ :db/fn (fn [db] (inc 1))}]
201
+ ''', input_format='edn')
202
+ ```
203
+
204
+ ```javascript
205
+ // JavaScript
206
+ await d.transact(conn, `
207
+ [{:db/id #db/id[:db.part/user]
208
+ :db/fn (fn [db] (inc 1))}]
209
+ `);
210
+ ```
211
+
212
+ ```java
213
+ // Java
214
+ Datahike.transact(conn,
215
+ "[{:db/id #db/id[:db.part/user] " +
216
+ " :db/fn (fn [db] (inc 1))}]");
217
+ ```
218
+
219
+ ## Custom Backend Support
220
+
221
+ Custom storage backends may require arbitrary configuration keys that are not known in advance. The map/dict/object-based API handles these naturally:
222
+
223
+ ```python
224
+ # Python - custom S3 backend
225
+ db = Database({
226
+ "store": {
227
+ "backend": ":my-s3",
228
+ "bucket": "my-bucket",
229
+ "region": "us-west-2",
230
+ "encryption": {
231
+ "type": ":aes256",
232
+ "key-id": "secret-key"
233
+ }
234
+ }
235
+ })
236
+ ```
237
+
238
+ ```javascript
239
+ // JavaScript - custom S3 backend
240
+ const db = new Database({
241
+ store: {
242
+ backend: ':my-s3',
243
+ bucket: 'my-bucket',
244
+ region: 'us-west-2',
245
+ encryption: {
246
+ type: ':aes256',
247
+ 'key-id': 'secret-key'
248
+ }
249
+ }
250
+ });
251
+ ```
252
+
253
+ ```java
254
+ // Java - custom S3 backend (Map API)
255
+ Map<String, Object> config = Map.of(
256
+ "store", Map.of(
257
+ "backend", ":my-s3",
258
+ "bucket", "my-bucket",
259
+ "region", "us-west-2",
260
+ "encryption", Map.of(
261
+ "type", ":aes256",
262
+ "key-id", "secret-key"
263
+ )
264
+ )
265
+ );
266
+ Database db = new Database(config);
267
+ ```
268
+
269
+ ## Common Keyword Constants
270
+
271
+ All language bindings provide pre-defined constants for frequently-used Datahike keywords:
272
+
273
+ ```python
274
+ # Python
275
+ from datahike import kw
276
+
277
+ kw.DB_ID # :db/id
278
+ kw.DB_IDENT # :db/ident
279
+ kw.DB_VALUE_TYPE # :db/valueType
280
+ kw.DB_CARDINALITY # :db/cardinality
281
+ kw.STRING # :db.type/string
282
+ kw.LONG # :db.type/long
283
+ kw.REF # :db.type/ref
284
+ kw.ONE # :db.cardinality/one
285
+ kw.MANY # :db.cardinality/many
286
+ ```
287
+
288
+ ```javascript
289
+ // JavaScript
290
+ import {kw} from 'datahike';
291
+
292
+ kw.DB_ID // :db/id
293
+ kw.DB_IDENT // :db/ident
294
+ // ... etc
295
+ ```
296
+
297
+ ```java
298
+ // Java
299
+ import static datahike.edn.Keywords.*;
300
+
301
+ DB_ID // :db/id
302
+ DB_IDENT // :db/ident
303
+ // ... etc
304
+ ```
305
+
306
+ ## Design Rationale
307
+
308
+ ### Why `:` prefix for keyword values?
309
+
310
+ **Explicit is better than implicit.** Previous versions used heuristics (e.g., `"backend": "mem"` → `:memory`), but this created ambiguity:
311
+ - Is `"person/name"` a keyword or a string with a slash?
312
+ - Is `"active"` a keyword or a string?
313
+
314
+ The `:` prefix makes the distinction unambiguous and works consistently across all contexts.
315
+
316
+ ### Why not separate config rules from data rules?
317
+
318
+ **Consistency reduces cognitive load.** Having one universal rule means:
319
+ - No need to remember context-specific rules
320
+ - Easier to teach and document
321
+ - Predictable behavior everywhere
322
+
323
+ ### Why backslash escaping?
324
+
325
+ **Familiar to programmers.** Backslash escaping is used in JSON, regex, shell scripts, and most programming languages. It's a well-understood convention.
326
+
327
+ ### Why allow EDN string fallback?
328
+
329
+ **Pragmatic escape hatch.** While the conversion rules handle 99% of cases, edge cases exist (database functions, unusual Clojure syntax). Allowing raw EDN strings ensures users are never blocked.
330
+
331
+ ## Migration from Previous Versions
332
+
333
+ ### JavaScript (Breaking Change)
334
+
335
+ **Old Syntax (v0.6 and earlier):**
336
+ ```javascript
337
+ const crypto = require('crypto');
338
+ const config = {
339
+ store: {
340
+ backend: 'memory', // String → keyword (implicit)
341
+ id: crypto.randomUUID() // Memory backend requires UUID
342
+ }
343
+ };
344
+ ```
345
+
346
+ **New Syntax (v0.7+):**
347
+ ```javascript
348
+ const crypto = require('crypto');
349
+ const config = {
350
+ store: {
351
+ backend: ':memory', // Explicit : required
352
+ id: crypto.randomUUID() // Memory backend requires UUID
353
+ }
354
+ };
355
+ ```
356
+
357
+ **Migration:** Add `:` prefix to all values that should be keywords. Note that memory backend requires UUID identifiers.
358
+
359
+ ### Java (Enhancement)
360
+
361
+ **Syntax:**
362
+ ```java
363
+ import java.util.UUID;
364
+
365
+ Map<String, Object> config = Map.of(
366
+ "store", Map.of(
367
+ "backend", ":memory", // Explicit : for keyword
368
+ "id", UUID.randomUUID().toString() // Memory backend requires UUID
369
+ )
370
+ );
371
+
372
+ // Or use builders (memory backend requires UUID)
373
+ Database.memory(UUID.randomUUID().toString()).build();
374
+ ```
375
+
376
+ **Migration:** Add `:` prefix to all values that should be keywords for consistency with other languages.
377
+
378
+ ## See Also
379
+
380
+ - [Python Binding Documentation](python.md)
381
+ - [Java Binding Documentation](java.md)
382
+ - [JavaScript Binding Documentation](javascript.md)
383
+ - [Custom Backends Guide](custom-backends.md)
package/doc/cli.md ADDED
@@ -0,0 +1,162 @@
1
+ # Command line interface
2
+
3
+ **Status: Beta** - The CLI is functional and tested, but the command structure may change as we refine the interface.
4
+
5
+ We provide the `dthk` native executable to access Datahike databases from
6
+ the command line.
7
+
8
+ ## Installation
9
+
10
+ 1. Download the precompiled binary for your platform from the [Datahike releases page](https://github.com/replikativ/datahike/releases)
11
+ 2. Unzip the archive
12
+ 3. Add the `dthk` executable to your PATH
13
+
14
+ Supported platforms:
15
+ - Linux (amd64, aarch64)
16
+ - macOS (aarch64/Apple Silicon)
17
+
18
+ For other platforms, build from source using GraalVM native-image (see project documentation).
19
+
20
+ ## Commands
21
+
22
+ The CLI provides flat commands that mirror the Clojure API functions. To see all available commands, run:
23
+
24
+ ```bash
25
+ $ dthk --help
26
+ ```
27
+
28
+ Common commands by category:
29
+ - **Database Operations**: `create-database`, `delete-database`, `database-exists`
30
+ - **Transaction Operations**: `transact`, `load-entities`, `db-with`, `with`
31
+ - **Query Operations**: `query`, `pull`, `pull-many`, `entity`, `datoms`, `seek-datoms`, `index-range`, `is-filtered`, `query-stats`
32
+ - **Schema Operations**: `schema`, `reverse-schema`
33
+ - **Diagnostics**: `metrics`
34
+ - **Maintenance**: `gc-storage`
35
+
36
+ Prefix syntax for database access:
37
+ - `db:config.edn` - Dereferences a connection to get the current database value
38
+ - `conn:config.edn` - Creates a connection for transacting
39
+ - `edn:file.edn` - Reads EDN data from a file
40
+ - `json:file.json` - Reads JSON data from a file
41
+ - `asof:timestamp:config.edn` - Creates a database snapshot as-of timestamp
42
+ - `since:timestamp:config.edn` - Creates a database view since timestamp
43
+ - `history:config.edn` - Returns the history database
44
+
45
+ ## Example usage
46
+
47
+ To access a database you need to provide the usual configuration for Datahike.
48
+ Put this into a file `myconfig.edn`.
49
+
50
+ ```clojure
51
+ {:store {:backend :file
52
+ :path "/home/USERNAME/dh-shared-db"
53
+ :id #uuid "550e8400-e29b-41d4-a716-446655440000"
54
+ :config {:in-place? true}}
55
+ :keep-history? true
56
+ :schema-flexibility :read}
57
+ ```
58
+
59
+ Now you can invoke some of our core API functions on the database. Let us add a
60
+ fact to the database (be careful to use single ' if you do not want your shell
61
+ to substitute parts of your Datalog ;) ):
62
+
63
+ ```bash
64
+ $ dthk transact conn:myconfig.edn '[[:db/add -1 :name "Linus"]]'
65
+ ```
66
+
67
+ And retrieve it:
68
+
69
+ ```bash
70
+ $ dthk query '[:find ?n . :where [?e :name ?n]]' db:myconfig.edn
71
+ "Linus" # prints the name
72
+ ```
73
+
74
+ Note that the `conn:<file>` argument to `transact` comes before the transaction
75
+ value(s), whereas the `db:<file>` argument to `q` comes after the query
76
+ value, mirroring the Clojure API. As an added benefit, this also allows passing
77
+ multiple db configuration files prefixed with `db:` for joining over arbitrary
78
+ many databases or data files with "edn:" or "json:". Everything non-prefixed is
79
+ read in as `edn` and passed to the query engine as well:
80
+
81
+ ```bash
82
+ $ dthk query '[:find ?e . :in $ ?name :where [?e :name ?name]]' db:myconfig.edn '"Linus"'
83
+ 123
84
+ ```
85
+
86
+ When passing strings as EDN, make sure to enclose double quotes as part of the
87
+ command-line arg value. Otherwise it will be parsed as a symbol.
88
+
89
+ Provided the filestore is configured with `{:in-place? true}` you can even write
90
+ to the same database without a dedicated daemon from different shells:
91
+
92
+ ```bash
93
+ # In the first shell
94
+ $ dthk transact conn:myconfig.edn '[[:db/add -1 :name "Alice"]]'
95
+
96
+ # In a second shell simultaneously
97
+ $ dthk transact conn:myconfig.edn '[[:db/add -2 :name "Bob"]]'
98
+ ```
99
+
100
+ To check that everything has been added and no write operations have overwritten
101
+ each other:
102
+
103
+ ```bash
104
+ $ dthk query '[:find (count ?e) . :in $ :where [?e :name ?n]]' db:myconfig.edn
105
+ 2 # check :)
106
+ ```
107
+
108
+ # Memory model
109
+
110
+ The persistent semantics of Datahike work more like `git` and less like similar
111
+ mutable databases such as SQLite or Datalevin. In particular you can always read
112
+ and retain snapshots (copies) of the database for free, no matter what else is
113
+ happening in the system. The current version is tested with memory and file
114
+ storage, but hopefully many other backends will also work with the
115
+ `native-image`.
116
+
117
+ In principle this shared memory access should even work while having a JVM
118
+ server, e.g. datahike-server, serving the same database. Note that all reads can
119
+ happen in parallel, only the writers experience congestion around exclusive file
120
+ locks here. This access pattern does not provide highest throughput, but is
121
+ extremely flexible and easy to start with.
122
+
123
+ ## Forking and pulling
124
+
125
+ Forking is easy, it is enough to copy the folder of the store (even if the
126
+ database is currently being written to). The only thing you need to take care of
127
+ is to copy the DB root first and place it into the target directory last. The root
128
+ file is a konserve internal storage file with a UUID name like `0594e3b6-9635-5c99-8142-412accf3023b.ksv`
129
+ (the actual UUID will match your database's `:id` configuration). Then you can use e.g.
130
+ `rsync` (or `git`) to copy all other (immutable) files into your new folder. In
131
+ the end you copy the root file in there as well, making sure that all files it
132
+ is referencing are reachable. Note that this will ensure that you only copy new
133
+ data each time.
134
+
135
+ ## Merging
136
+
137
+ Now here comes the cool part. You do not need anything more for merging than
138
+ Datalog itself. You can use a query like this to extract all new facts that are
139
+ in `db1` but not in `db2` like this:
140
+
141
+ ```bash
142
+ dthk query '[:find ?e ?a ?v ?t :in $ $2 :where [$ ?e ?a ?v ?t] (not [$2 ?e ?a ?v ?t])]' db:config1.edn db:config2.edn
143
+ ```
144
+
145
+ Since we cannot update transaction metadata, we should filter out
146
+ `:db/txInstant`s. We can also use a trick to add `:db/add` to each element in
147
+ the results, yielding valid transactions that we can then feed into `db2`.
148
+
149
+
150
+ ```bash
151
+ dthk query '[:find ?db-add ?e ?a ?v ?t :in $ $2 ?db-add :where [$ ?e ?a ?v ?t] [(not= :db/txInstant ?a)] (not [$2 ?e ?a ?v ?t])]' db:config1.edn db:config2.edn ":db/add" | dthk transact db:config2.edn
152
+ ```
153
+
154
+ Note that this very simple strategy assumes that the entity ids that have been
155
+ added to `db1` do not overlap with potentially new ones added to `db2`. You can
156
+ encode conflict resolution strategies and id mappings with Datalog as well and
157
+ we are exploring several such strategies at the moment. This strategy is fairly
158
+ universal, as [CRDTs can be expressed in pure
159
+ Datalog](https://speakerdeck.com/ept/data-structures-as-queries-expressing-crdts-using-datalog).
160
+ While it is not the most efficient way to merge, we plan to provide fast paths
161
+ for common patterns in Datalog. Feel free to contact us if you are interested in
162
+ complex merging strategies or have related cool ideas.
package/doc/cljdoc.edn ADDED
@@ -0,0 +1,27 @@
1
+ {:cljdoc.doc/tree [["Readme" {:file "README.md"}]
2
+ ["Getting Started" {}
3
+ ["Why Datalog?" {:file "doc/datalog-vs-sql.md"}]
4
+ ["Configuration" {:file "doc/config.md"}]
5
+ ["Storage Backends" {:file "doc/storage-backends.md"}]
6
+ ["Schema" {:file "doc/schema.md"}]]
7
+ ["Core Features" {}
8
+ ["Time Variance" {:file "doc/time_variance.md"}]
9
+ ["Versioning (Beta)" {:file "doc/versioning.md"}]
10
+ ["Distributed Architecture" {:file "doc/distributed.md"}]
11
+ ["Garbage Collection" {:file "doc/gc.md"}]
12
+ ["Norms (Database Migrations)" {:file "doc/norms.md"}]]
13
+ ["Platform Support" {}
14
+ ["ClojureScript Support" {:file "doc/cljs-support.md"}]
15
+ ["JavaScript API" {:file "doc/javascript-api.md"}]
16
+ ["Babashka Pod" {:file "doc/bb-pod.md"}]
17
+ ["Command Line Interface" {:file "doc/cli.md"}]
18
+ ["libdatahike (C/C++)" {:file "doc/libdatahike.md"}]]
19
+ ["Advanced" {}
20
+ ["Entity Specs" {:file "doc/entity_spec.md"}]
21
+ ["Logging and Error Handling" {:file "doc/logging_and_error_handling.md"}]
22
+ ["Unstructured Input (Experimental)" {:file "doc/unstructured.md"}]
23
+ ["Backend Development" {:file "doc/backend-development.md"}]
24
+ ["Benchmarking" {:file "doc/benchmarking.md"}]]
25
+ ["Reference" {}
26
+ ["Differences to Datomic" {:file "doc/datomic_differences.md"}]
27
+ ["Contributing" {:file "doc/contributing.md"}]]]}
@@ -0,0 +1,133 @@
1
+ # ClojureScript Support
2
+
3
+ **This feature is in beta. Please try it out and provide feedback.**
4
+
5
+ Datahike runs in ClojureScript on both Node.js and browser environments. The
6
+ core query engine, pull API, and entity API work identically to Clojure. The
7
+ main differences are in storage backends and async operation.
8
+
9
+ ## Async Storage Model
10
+
11
+ ClojureScript environments often require async I/O (IndexedDB, network). To
12
+ support this, Datahike's persistent-sorted-set indices use *partial
13
+ continuation-passing style* (partial-cps): storage operations return channels
14
+ instead of values, while in-memory operations remain synchronous.
15
+
16
+ This is transparent when using `datahike.api` - async backends return channels
17
+ from `create-database`, `connect`, `transact!`, etc.
18
+
19
+ ## Node.js
20
+
21
+ Node.js supports the file backend via
22
+ [konserve-node-filestore](https://github.com/replikativ/konserve):
23
+
24
+ ```clojure
25
+ (ns my-app.core
26
+ (:require [datahike.api :as d]
27
+ [datahike.nodejs] ;; Registers :file backend
28
+ [cljs.core.async :refer [<!] :refer-macros [go]]))
29
+
30
+ (go
31
+ (let [config {:store {:backend :file
32
+ :path "./my-database"
33
+ :id #uuid "550e8400-e29b-41d4-a716-446655440000"}
34
+ :schema-flexibility :write}]
35
+ (<! (d/create-database config))
36
+ (let [conn (<! (d/connect config))]
37
+ (<! (d/transact! conn [{:name "Alice"}]))
38
+ (println (d/q '[:find ?n :where [?e :name ?n]] (d/db conn))))))
39
+ ```
40
+
41
+ ## Browser
42
+
43
+ Browsers cannot use file storage directly. Datahike provides two browser-compatible backends:
44
+
45
+ ### Memory backend
46
+
47
+ Fast but not persistent across page reloads:
48
+
49
+ ```clojure
50
+ {:store {:backend :memory :id #uuid "550e8400-e29b-41d4-a716-446655440000"}}
51
+ ```
52
+
53
+ ### IndexedDB backend
54
+
55
+ Persistent browser storage via
56
+ [konserve-indexeddb](https://github.com/replikativ/konserve):
57
+
58
+ ```clojure
59
+ {:store {:backend :indexeddb :id #uuid "550e8400-e29b-41d4-a716-446655440000"}}
60
+ ```
61
+
62
+ IndexedDB is async, so all operations return channels.
63
+
64
+ ### TieredStore
65
+
66
+ For optimal performance, use TieredStore which combines a fast memory frontend
67
+ with a persistent IndexedDB backend:
68
+
69
+ ```clojure
70
+ {:store {:backend :tiered
71
+ :id #uuid "550e8400-e29b-41d4-a716-446655440000"
72
+ :frontend-config {:backend :memory
73
+ :id #uuid "550e8400-e29b-41d4-a716-446655440000"}
74
+ :backend-config {:backend :indexeddb
75
+ :id #uuid "550e8400-e29b-41d4-a716-446655440000"}}}
76
+ ```
77
+
78
+ Writes go to both stores (write-through). Reads come from memory. On
79
+ reconnection, TieredStore syncs cached data from IndexedDB into memory before
80
+ the sync handshake, so only keys newer than cached timestamps are transferred.
81
+
82
+ ## Distributed Setup with KabelWriter
83
+
84
+ For browser applications that need a persistent backend, use the KabelWriter to
85
+ connect to a JVM server. The server owns the database (using file storage) and
86
+ streams updates to browser clients via WebSockets. See [Streaming writer
87
+ (Kabel)](distributed.md#streaming-writer-kabel) for setup instructions.
88
+
89
+ This architecture means:
90
+ - Transactions are sent to the server via RPC
91
+ - The server writes to its file store
92
+ - Updates are replicated to the client's TieredStore via konserve-sync
93
+ - Queries run locally against the synchronized in-memory store
94
+
95
+ ## JavaScript API
96
+
97
+ For JavaScript/TypeScript applications, Datahike provides a Promise-based API.
98
+ See [JavaScript API](javascript-api.md) for documentation.
99
+
100
+ **Installation:**
101
+ ```bash
102
+ npm install datahike@next
103
+ ```
104
+
105
+ **Example:**
106
+ ```javascript
107
+ const d = require('datahike');
108
+ const crypto = require('crypto');
109
+
110
+ const config = {
111
+ store: {
112
+ backend: ':memory',
113
+ id: crypto.randomUUID()
114
+ },
115
+ 'schema-flexibility': ':read' // Allow schemaless data (use kebab-case)
116
+ };
117
+
118
+ await d.createDatabase(config);
119
+ const conn = await d.connect(config);
120
+ await d.transact(conn, [{ name: 'Alice' }]);
121
+ const db = await d.db(conn); // db() is async for async backends
122
+ const results = await d.q('[:find ?n :where [?e :name ?n]]', db);
123
+ console.log(results);
124
+ // => [['Alice']]
125
+ ```
126
+
127
+ ## Limitations
128
+
129
+ - **History queries**: Work but may be slower due to async index traversal
130
+ - **Large datasets**: Browser memory constraints apply; consider server-side
131
+ queries for large result sets
132
+ - **Concurrent tabs**: Each tab has its own connection; use KabelWriter for
133
+ cross-tab consistency