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.
- package/.circleci/config.yml +405 -0
- package/.circleci/scripts/gen_ci.clj +194 -0
- package/.cirrus.yml +60 -0
- package/.clj-kondo/babashka/sci/config.edn +1 -0
- package/.clj-kondo/babashka/sci/sci/core.clj +9 -0
- package/.clj-kondo/config.edn +95 -0
- package/.dir-locals.el +2 -0
- package/.github/FUNDING.yml +3 -0
- package/.github/ISSUE_TEMPLATE/1-bug-report.yml +68 -0
- package/.github/ISSUE_TEMPLATE/2-feature-request.yml +28 -0
- package/.github/ISSUE_TEMPLATE/config.yml +6 -0
- package/.github/pull_request_template.md +24 -0
- package/.github/workflows/native-image.yml +84 -0
- package/LICENSE +203 -0
- package/README.md +273 -0
- package/bb/deps.edn +9 -0
- package/bb/resources/github-fingerprints +3 -0
- package/bb/resources/native-image-tests/run-bb-pod-tests.clj +162 -0
- package/bb/resources/native-image-tests/run-libdatahike-tests +12 -0
- package/bb/resources/native-image-tests/run-native-image-tests +74 -0
- package/bb/resources/native-image-tests/run-python-tests +22 -0
- package/bb/resources/native-image-tests/testconfig.attr-refs.edn +6 -0
- package/bb/resources/native-image-tests/testconfig.edn +5 -0
- package/bb/resources/template/.settings/org.eclipse.jdt.apt.core.prefs +2 -0
- package/bb/resources/template/.settings/org.eclipse.jdt.core.prefs +9 -0
- package/bb/resources/template/.settings/org.eclipse.m2e.core.prefs +4 -0
- package/bb/resources/template/pom.xml +22 -0
- package/bb/src/tools/build.clj +132 -0
- package/bb/src/tools/clj_kondo.clj +32 -0
- package/bb/src/tools/deploy.clj +26 -0
- package/bb/src/tools/examples.clj +19 -0
- package/bb/src/tools/npm.clj +100 -0
- package/bb/src/tools/python.clj +14 -0
- package/bb/src/tools/release.clj +94 -0
- package/bb/src/tools/test.clj +148 -0
- package/bb/src/tools/version.clj +47 -0
- package/bb.edn +269 -0
- package/benchmark/src/benchmark/cli.clj +195 -0
- package/benchmark/src/benchmark/compare.clj +157 -0
- package/benchmark/src/benchmark/config.clj +316 -0
- package/benchmark/src/benchmark/measure.clj +187 -0
- package/benchmark/src/benchmark/store.clj +190 -0
- package/benchmark/test/benchmark/measure_test.clj +156 -0
- package/build.clj +30 -0
- package/config.edn +49 -0
- package/deps.edn +138 -0
- package/dev/sandbox.clj +82 -0
- package/dev/sandbox.cljs +127 -0
- package/dev/sandbox_benchmarks.clj +27 -0
- package/dev/sandbox_client.clj +87 -0
- package/dev/sandbox_transact_bench.clj +109 -0
- package/dev/user.clj +79 -0
- package/doc/README.md +96 -0
- package/doc/adl/README.md +6 -0
- package/doc/adl/adr-000-adr.org +28 -0
- package/doc/adl/adr-001-attribute-references.org +15 -0
- package/doc/adl/adr-002-build-tooling.org +54 -0
- package/doc/adl/adr-003-db-meta-data.md +52 -0
- package/doc/adl/adr-004-github-flow.md +40 -0
- package/doc/adl/adr-XYZ-template.md +30 -0
- package/doc/adl/index.org +3 -0
- package/doc/assets/datahike-logo.svg +3 -0
- package/doc/assets/datahiking-invoice.org +85 -0
- package/doc/assets/hhtree2.png +0 -0
- package/doc/assets/network_topology.svg +624 -0
- package/doc/assets/perf.png +0 -0
- package/doc/assets/schema_mindmap.mm +132 -0
- package/doc/assets/schema_mindmap.svg +970 -0
- package/doc/assets/temporal_index.mm +74 -0
- package/doc/backend-development.md +78 -0
- package/doc/bb-pod.md +89 -0
- package/doc/benchmarking.md +360 -0
- package/doc/bindings/edn-conversion.md +383 -0
- package/doc/cli.md +162 -0
- package/doc/cljdoc.edn +27 -0
- package/doc/cljs-support.md +133 -0
- package/doc/config.md +406 -0
- package/doc/contributing.md +114 -0
- package/doc/datalog-vs-sql.md +210 -0
- package/doc/datomic_differences.md +109 -0
- package/doc/development/pull-api-ns.md +186 -0
- package/doc/development/pull-frame-state-diagram.jpg +0 -0
- package/doc/distributed.md +566 -0
- package/doc/entity_spec.md +92 -0
- package/doc/gc.md +273 -0
- package/doc/java-api.md +808 -0
- package/doc/javascript-api.md +421 -0
- package/doc/libdatahike.md +86 -0
- package/doc/logging_and_error_handling.md +43 -0
- package/doc/norms.md +66 -0
- package/doc/schema-migration.md +85 -0
- package/doc/schema.md +287 -0
- package/doc/storage-backends.md +363 -0
- package/doc/store-id-refactoring.md +596 -0
- package/doc/time_variance.md +325 -0
- package/doc/unstructured.md +167 -0
- package/doc/versioning.md +261 -0
- package/examples/basic/README.md +19 -0
- package/examples/basic/deps.edn +6 -0
- package/examples/basic/docker-compose.yml +13 -0
- package/examples/basic/src/examples/core.clj +60 -0
- package/examples/basic/src/examples/schema.clj +155 -0
- package/examples/basic/src/examples/store.clj +60 -0
- package/examples/basic/src/examples/time_travel.clj +185 -0
- package/examples/java/.settings/org.eclipse.core.resources.prefs +3 -0
- package/examples/java/.settings/org.eclipse.jdt.apt.core.prefs +2 -0
- package/examples/java/.settings/org.eclipse.jdt.core.prefs +9 -0
- package/examples/java/.settings/org.eclipse.m2e.core.prefs +4 -0
- package/examples/java/README.md +162 -0
- package/examples/java/pom.xml +62 -0
- package/examples/java/src/main/java/examples/QuickStart.java +115 -0
- package/examples/java/src/main/java/examples/SchemaExample.java +148 -0
- package/examples/java/src/main/java/examples/TimeTravelExample.java +121 -0
- package/flake.lock +27 -0
- package/flake.nix +27 -0
- package/http-server/datahike/http/middleware.clj +75 -0
- package/http-server/datahike/http/server.clj +269 -0
- package/java/src/datahike/java/Database.java +274 -0
- package/java/src/datahike/java/Datahike.java +281 -0
- package/java/src/datahike/java/DatahikeGeneratedTest.java +349 -0
- package/java/src/datahike/java/DatahikeTest.java +370 -0
- package/java/src/datahike/java/EDN.java +170 -0
- package/java/src/datahike/java/IEntity.java +11 -0
- package/java/src/datahike/java/Keywords.java +161 -0
- package/java/src/datahike/java/SchemaFlexibility.java +52 -0
- package/java/src/datahike/java/Util.java +219 -0
- package/karma.conf.js +19 -0
- package/libdatahike/compile-cpp +7 -0
- package/libdatahike/src/datahike/impl/LibDatahikeBase.java +203 -0
- package/libdatahike/src/datahike/impl/libdatahike.clj +59 -0
- package/libdatahike/src/test_cpp.cpp +61 -0
- package/npm-package/PUBLISHING.md +140 -0
- package/npm-package/README.md +226 -0
- package/npm-package/package.template.json +34 -0
- package/npm-package/test-isomorphic.ts +281 -0
- package/npm-package/test.js +557 -0
- package/npm-package/typescript-test.ts +70 -0
- package/package.json +16 -0
- package/pydatahike/README.md +569 -0
- package/pydatahike/pyproject.toml +91 -0
- package/pydatahike/setup.py +42 -0
- package/pydatahike/src/datahike/__init__.py +134 -0
- package/pydatahike/src/datahike/_native.py +250 -0
- package/pydatahike/src/datahike/_version.py +2 -0
- package/pydatahike/src/datahike/database.py +722 -0
- package/pydatahike/src/datahike/edn.py +311 -0
- package/pydatahike/src/datahike/py.typed +0 -0
- package/pydatahike/tests/conftest.py +17 -0
- package/pydatahike/tests/test_basic.py +170 -0
- package/pydatahike/tests/test_database.py +51 -0
- package/pydatahike/tests/test_edn_conversion.py +299 -0
- package/pydatahike/tests/test_query.py +99 -0
- package/pydatahike/tests/test_schema.py +55 -0
- package/resources/clj-kondo.exports/io.replikativ/datahike/config.edn +5 -0
- package/resources/example_server.edn +4 -0
- package/shadow-cljs.edn +56 -0
- package/src/data_readers.clj +7 -0
- package/src/datahike/api/impl.cljc +176 -0
- package/src/datahike/api/specification.cljc +633 -0
- package/src/datahike/api/types.cljc +261 -0
- package/src/datahike/api.cljc +41 -0
- package/src/datahike/array.cljc +99 -0
- package/src/datahike/cli.clj +166 -0
- package/src/datahike/cljs.cljs +6 -0
- package/src/datahike/codegen/cli.clj +406 -0
- package/src/datahike/codegen/clj_kondo.clj +291 -0
- package/src/datahike/codegen/java.clj +403 -0
- package/src/datahike/codegen/naming.cljc +33 -0
- package/src/datahike/codegen/native.clj +559 -0
- package/src/datahike/codegen/pod.clj +488 -0
- package/src/datahike/codegen/python.clj +838 -0
- package/src/datahike/codegen/report.clj +55 -0
- package/src/datahike/codegen/typescript.clj +262 -0
- package/src/datahike/codegen/validation.clj +145 -0
- package/src/datahike/config.cljc +294 -0
- package/src/datahike/connections.cljc +16 -0
- package/src/datahike/connector.cljc +265 -0
- package/src/datahike/constants.cljc +142 -0
- package/src/datahike/core.cljc +297 -0
- package/src/datahike/datom.cljc +459 -0
- package/src/datahike/db/interface.cljc +119 -0
- package/src/datahike/db/search.cljc +305 -0
- package/src/datahike/db/transaction.cljc +937 -0
- package/src/datahike/db/utils.cljc +338 -0
- package/src/datahike/db.cljc +956 -0
- package/src/datahike/experimental/unstructured.cljc +126 -0
- package/src/datahike/experimental/versioning.cljc +172 -0
- package/src/datahike/externs.js +31 -0
- package/src/datahike/gc.cljc +69 -0
- package/src/datahike/http/client.clj +188 -0
- package/src/datahike/http/writer.clj +79 -0
- package/src/datahike/impl/entity.cljc +218 -0
- package/src/datahike/index/interface.cljc +93 -0
- package/src/datahike/index/persistent_set.cljc +469 -0
- package/src/datahike/index/utils.cljc +44 -0
- package/src/datahike/index.cljc +32 -0
- package/src/datahike/js/api.cljs +172 -0
- package/src/datahike/js/api_macros.clj +22 -0
- package/src/datahike/js.cljs +163 -0
- package/src/datahike/json.cljc +209 -0
- package/src/datahike/lru.cljc +146 -0
- package/src/datahike/migrate.clj +39 -0
- package/src/datahike/norm/norm.clj +245 -0
- package/src/datahike/online_gc.cljc +252 -0
- package/src/datahike/pod.clj +155 -0
- package/src/datahike/pull_api.cljc +325 -0
- package/src/datahike/query.cljc +1945 -0
- package/src/datahike/query_stats.cljc +88 -0
- package/src/datahike/readers.cljc +62 -0
- package/src/datahike/remote.cljc +218 -0
- package/src/datahike/schema.cljc +228 -0
- package/src/datahike/schema_cache.cljc +42 -0
- package/src/datahike/spec.cljc +101 -0
- package/src/datahike/store.cljc +80 -0
- package/src/datahike/tools.cljc +308 -0
- package/src/datahike/transit.cljc +80 -0
- package/src/datahike/writer.cljc +239 -0
- package/src/datahike/writing.cljc +362 -0
- package/src/deps.cljs +1 -0
- package/src-hitchhiker-tree/datahike/index/hitchhiker_tree/insert.cljc +76 -0
- package/src-hitchhiker-tree/datahike/index/hitchhiker_tree/upsert.cljc +128 -0
- package/src-hitchhiker-tree/datahike/index/hitchhiker_tree.cljc +213 -0
- package/test/datahike/backward_compatibility_test/src/backward_test.clj +37 -0
- package/test/datahike/integration_test/config_record_file_test.clj +14 -0
- package/test/datahike/integration_test/config_record_test.clj +14 -0
- package/test/datahike/integration_test/depr_config_uri_test.clj +15 -0
- package/test/datahike/integration_test/return_map_test.clj +62 -0
- package/test/datahike/integration_test.cljc +67 -0
- package/test/datahike/norm/norm_test.clj +124 -0
- package/test/datahike/norm/resources/naming-and-sorting-test/001-a1-example.edn +5 -0
- package/test/datahike/norm/resources/naming-and-sorting-test/002-a2-example.edn +5 -0
- package/test/datahike/norm/resources/naming-and-sorting-test/003-tx-fn-test.edn +1 -0
- package/test/datahike/norm/resources/naming-and-sorting-test/004-tx-data-and-tx-fn-test.edn +5 -0
- package/test/datahike/norm/resources/naming-and-sorting-test/01-transact-basic-characters.edn +2 -0
- package/test/datahike/norm/resources/naming-and-sorting-test/02 add occupation.edn +5 -0
- package/test/datahike/norm/resources/naming-and-sorting-test/checksums.edn +12 -0
- package/test/datahike/norm/resources/simple-test/001-a1-example.edn +5 -0
- package/test/datahike/norm/resources/simple-test/002-a2-example.edn +5 -0
- package/test/datahike/norm/resources/simple-test/checksums.edn +4 -0
- package/test/datahike/norm/resources/tx-data-and-tx-fn-test/first/001-a1-example.edn +5 -0
- package/test/datahike/norm/resources/tx-data-and-tx-fn-test/first/002-a2-example.edn +5 -0
- package/test/datahike/norm/resources/tx-data-and-tx-fn-test/first/003-tx-fn-test.edn +1 -0
- package/test/datahike/norm/resources/tx-data-and-tx-fn-test/first/checksums.edn +6 -0
- package/test/datahike/norm/resources/tx-data-and-tx-fn-test/second/004-tx-data-and-tx-fn-test.edn +5 -0
- package/test/datahike/norm/resources/tx-data-and-tx-fn-test/second/checksums.edn +2 -0
- package/test/datahike/norm/resources/tx-fn-test/first/001-a1-example.edn +5 -0
- package/test/datahike/norm/resources/tx-fn-test/first/002-a2-example.edn +5 -0
- package/test/datahike/norm/resources/tx-fn-test/first/checksums.edn +4 -0
- package/test/datahike/norm/resources/tx-fn-test/second/003-tx-fn-test.edn +1 -0
- package/test/datahike/norm/resources/tx-fn-test/second/checksums.edn +2 -0
- package/test/datahike/test/api_test.cljc +895 -0
- package/test/datahike/test/array_test.cljc +40 -0
- package/test/datahike/test/attribute_refs/datoms_test.cljc +140 -0
- package/test/datahike/test/attribute_refs/db_test.cljc +42 -0
- package/test/datahike/test/attribute_refs/differences_test.cljc +515 -0
- package/test/datahike/test/attribute_refs/entity_test.cljc +89 -0
- package/test/datahike/test/attribute_refs/pull_api_test.cljc +320 -0
- package/test/datahike/test/attribute_refs/query_find_specs_test.cljc +59 -0
- package/test/datahike/test/attribute_refs/query_fns_test.cljc +130 -0
- package/test/datahike/test/attribute_refs/query_interop_test.cljc +47 -0
- package/test/datahike/test/attribute_refs/query_not_test.cljc +193 -0
- package/test/datahike/test/attribute_refs/query_or_test.cljc +137 -0
- package/test/datahike/test/attribute_refs/query_pull_test.cljc +156 -0
- package/test/datahike/test/attribute_refs/query_rules_test.cljc +176 -0
- package/test/datahike/test/attribute_refs/query_test.cljc +241 -0
- package/test/datahike/test/attribute_refs/temporal_search.cljc +22 -0
- package/test/datahike/test/attribute_refs/transact_test.cljc +220 -0
- package/test/datahike/test/attribute_refs/utils.cljc +128 -0
- package/test/datahike/test/cache_test.cljc +38 -0
- package/test/datahike/test/components_test.cljc +92 -0
- package/test/datahike/test/config_test.cljc +158 -0
- package/test/datahike/test/core_test.cljc +105 -0
- package/test/datahike/test/datom_test.cljc +44 -0
- package/test/datahike/test/db_test.cljc +54 -0
- package/test/datahike/test/entity_spec_test.cljc +159 -0
- package/test/datahike/test/entity_test.cljc +103 -0
- package/test/datahike/test/explode_test.cljc +143 -0
- package/test/datahike/test/filter_test.cljc +75 -0
- package/test/datahike/test/gc_test.cljc +159 -0
- package/test/datahike/test/http/server_test.clj +192 -0
- package/test/datahike/test/http/writer_test.clj +86 -0
- package/test/datahike/test/ident_test.cljc +32 -0
- package/test/datahike/test/index_test.cljc +345 -0
- package/test/datahike/test/insert.cljc +125 -0
- package/test/datahike/test/java_bindings_test.clj +6 -0
- package/test/datahike/test/listen_test.cljc +41 -0
- package/test/datahike/test/lookup_refs_test.cljc +266 -0
- package/test/datahike/test/lru_test.cljc +27 -0
- package/test/datahike/test/migrate_test.clj +297 -0
- package/test/datahike/test/model/core.cljc +376 -0
- package/test/datahike/test/model/invariant.cljc +142 -0
- package/test/datahike/test/model/rng.cljc +82 -0
- package/test/datahike/test/model_test.clj +217 -0
- package/test/datahike/test/nodejs_test.cljs +262 -0
- package/test/datahike/test/online_gc_test.cljc +475 -0
- package/test/datahike/test/pod_test.clj +369 -0
- package/test/datahike/test/pull_api_test.cljc +474 -0
- package/test/datahike/test/purge_test.cljc +144 -0
- package/test/datahike/test/query_aggregates_test.cljc +101 -0
- package/test/datahike/test/query_find_specs_test.cljc +52 -0
- package/test/datahike/test/query_fns_test.cljc +523 -0
- package/test/datahike/test/query_interop_test.cljc +47 -0
- package/test/datahike/test/query_not_test.cljc +189 -0
- package/test/datahike/test/query_or_test.cljc +158 -0
- package/test/datahike/test/query_pull_test.cljc +147 -0
- package/test/datahike/test/query_rules_test.cljc +248 -0
- package/test/datahike/test/query_stats_test.cljc +218 -0
- package/test/datahike/test/query_test.cljc +984 -0
- package/test/datahike/test/schema_test.cljc +424 -0
- package/test/datahike/test/specification_test.cljc +30 -0
- package/test/datahike/test/store_test.cljc +78 -0
- package/test/datahike/test/stress_test.cljc +57 -0
- package/test/datahike/test/time_variance_test.cljc +518 -0
- package/test/datahike/test/tools_test.clj +134 -0
- package/test/datahike/test/transact_test.cljc +518 -0
- package/test/datahike/test/tuples_test.cljc +564 -0
- package/test/datahike/test/unstructured_test.cljc +291 -0
- package/test/datahike/test/upsert_impl_test.cljc +205 -0
- package/test/datahike/test/upsert_test.cljc +363 -0
- package/test/datahike/test/utils.cljc +110 -0
- package/test/datahike/test/validation_test.cljc +48 -0
- package/test/datahike/test/versioning_test.cljc +56 -0
- package/test/datahike/test.cljc +66 -0
- package/tests.edn +24 -0
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
(ns datahike.api.types
|
|
2
|
+
"Malli type schemas for Datahike API.
|
|
3
|
+
|
|
4
|
+
This namespace replaces datahike.spec with malli-based schemas.
|
|
5
|
+
Schemas are used for:
|
|
6
|
+
- API specification
|
|
7
|
+
- Runtime validation
|
|
8
|
+
- Code generation (Java, CLI, TypeScript)
|
|
9
|
+
- Documentation generation"
|
|
10
|
+
(:require [malli.core :as m]
|
|
11
|
+
[malli.util :as mu]
|
|
12
|
+
[malli.error :as me]
|
|
13
|
+
[datahike.connector :refer [connection?]]
|
|
14
|
+
[datahike.datom :refer [datom?]]
|
|
15
|
+
[datahike.db.utils :as dbu])
|
|
16
|
+
#?(:clj
|
|
17
|
+
(:import [java.util Date])))
|
|
18
|
+
|
|
19
|
+
;; =============================================================================
|
|
20
|
+
;; Helper Predicates
|
|
21
|
+
;; =============================================================================
|
|
22
|
+
|
|
23
|
+
(defn date? [d]
|
|
24
|
+
#?(:cljs (instance? js/Date d)
|
|
25
|
+
:clj (instance? Date d)))
|
|
26
|
+
|
|
27
|
+
;; ============================================================================
|
|
28
|
+
;; Core Type Schemas
|
|
29
|
+
;; =============================================================================
|
|
30
|
+
|
|
31
|
+
(def SDB
|
|
32
|
+
"Immutable database value."
|
|
33
|
+
[:fn {:error/message "must be a Datahike database"}
|
|
34
|
+
dbu/db?])
|
|
35
|
+
|
|
36
|
+
(def SConfig
|
|
37
|
+
"Database configuration.
|
|
38
|
+
|
|
39
|
+
Can be:
|
|
40
|
+
- Map with :store key (primary format)
|
|
41
|
+
- URI string (deprecated)"
|
|
42
|
+
[:or
|
|
43
|
+
[:map {:closed false}]
|
|
44
|
+
:string])
|
|
45
|
+
|
|
46
|
+
(def SConnection
|
|
47
|
+
"Database connection reference."
|
|
48
|
+
[:fn {:error/message "must be a Datahike connection"}
|
|
49
|
+
connection?])
|
|
50
|
+
|
|
51
|
+
(def SEId
|
|
52
|
+
"Entity identifier.
|
|
53
|
+
|
|
54
|
+
Can be:
|
|
55
|
+
- Number (entity ID)
|
|
56
|
+
- Sequential (lookup ref [:attr value])
|
|
57
|
+
- Keyword (for schema entities)"
|
|
58
|
+
[:or [:fn number?]
|
|
59
|
+
[:sequential :any]
|
|
60
|
+
:keyword])
|
|
61
|
+
|
|
62
|
+
(def SDatom
|
|
63
|
+
"Single datom (immutable fact)."
|
|
64
|
+
[:fn {:error/message "must be a Datom"}
|
|
65
|
+
datom?])
|
|
66
|
+
|
|
67
|
+
(def SDatoms
|
|
68
|
+
"Collection of datoms."
|
|
69
|
+
[:sequential SDatom])
|
|
70
|
+
|
|
71
|
+
(def STxMeta
|
|
72
|
+
"Transaction metadata - optional collection."
|
|
73
|
+
[:maybe :any])
|
|
74
|
+
|
|
75
|
+
(def STransactionReport
|
|
76
|
+
"Transaction result map.
|
|
77
|
+
|
|
78
|
+
Keys:
|
|
79
|
+
- :db-before - Database before transaction
|
|
80
|
+
- :db-after - Database after transaction
|
|
81
|
+
- :tx-data - Datoms added/retracted
|
|
82
|
+
- :tempids - Temp ID to entity ID mapping
|
|
83
|
+
- :tx-meta - Transaction metadata (optional)"
|
|
84
|
+
[:map
|
|
85
|
+
[:db-before SDB]
|
|
86
|
+
[:db-after SDB]
|
|
87
|
+
[:tx-data SDatoms]
|
|
88
|
+
[:tempids :map]
|
|
89
|
+
[:tx-meta {:optional true} STxMeta]])
|
|
90
|
+
|
|
91
|
+
(def STransactions
|
|
92
|
+
"Transaction data - collection of transaction forms."
|
|
93
|
+
[:sequential :any])
|
|
94
|
+
|
|
95
|
+
(def SPullOptions
|
|
96
|
+
"Pull pattern options map."
|
|
97
|
+
[:map
|
|
98
|
+
[:selector [:vector :any]]
|
|
99
|
+
[:eid SEId]])
|
|
100
|
+
|
|
101
|
+
(def SQueryArgs
|
|
102
|
+
"Query arguments map."
|
|
103
|
+
[:map
|
|
104
|
+
[:query [:or :string [:vector :any] :map]]
|
|
105
|
+
[:args {:optional true} [:sequential :any]]
|
|
106
|
+
[:limit {:optional true} :int]
|
|
107
|
+
[:offset {:optional true} :int]])
|
|
108
|
+
|
|
109
|
+
(def SWithArgs
|
|
110
|
+
"Arguments for 'with' operation."
|
|
111
|
+
[:map
|
|
112
|
+
[:tx-data STransactions]
|
|
113
|
+
[:tx-meta {:optional true} STxMeta]])
|
|
114
|
+
|
|
115
|
+
(def SIndexLookupArgs
|
|
116
|
+
"Index lookup arguments."
|
|
117
|
+
[:map
|
|
118
|
+
[:index [:enum :eavt :aevt :avet]]
|
|
119
|
+
[:components {:optional true} [:maybe [:sequential :any]]]])
|
|
120
|
+
|
|
121
|
+
(def SIndexRangeArgs
|
|
122
|
+
"Index range query arguments."
|
|
123
|
+
[:map
|
|
124
|
+
[:attrid :keyword]
|
|
125
|
+
[:start :any]
|
|
126
|
+
[:end :any]])
|
|
127
|
+
|
|
128
|
+
(def SSchema
|
|
129
|
+
"Database schema - map of attributes to schema entries."
|
|
130
|
+
[:map-of :any :any])
|
|
131
|
+
|
|
132
|
+
(def SMetrics
|
|
133
|
+
"Database metrics map."
|
|
134
|
+
[:map
|
|
135
|
+
[:count :int]
|
|
136
|
+
[:avet-count :int]
|
|
137
|
+
[:per-attr-counts :map]
|
|
138
|
+
[:per-entity-counts :map]
|
|
139
|
+
[:temporal-count {:optional true} :int]
|
|
140
|
+
[:temporal-avet-count {:optional true} :int]])
|
|
141
|
+
|
|
142
|
+
(def time-point?
|
|
143
|
+
"Time point - transaction ID (int) or Date."
|
|
144
|
+
[:or :int
|
|
145
|
+
[:fn {:error/message "must be a Date"}
|
|
146
|
+
date?]])
|
|
147
|
+
|
|
148
|
+
;; =============================================================================
|
|
149
|
+
;; Malli Registry
|
|
150
|
+
;; =============================================================================
|
|
151
|
+
|
|
152
|
+
(def registry
|
|
153
|
+
"Malli registry containing all Datahike type schemas.
|
|
154
|
+
This allows schemas to reference each other by keyword and enables
|
|
155
|
+
proper integration with reitit coercion."
|
|
156
|
+
(merge
|
|
157
|
+
(m/default-schemas)
|
|
158
|
+
{:datahike/SDB SDB
|
|
159
|
+
:datahike/SConfig SConfig
|
|
160
|
+
:datahike/SConnection SConnection
|
|
161
|
+
:datahike/SEId SEId
|
|
162
|
+
:datahike/SDatom SDatom
|
|
163
|
+
:datahike/SDatoms SDatoms
|
|
164
|
+
:datahike/STxMeta STxMeta
|
|
165
|
+
:datahike/STransactionReport STransactionReport
|
|
166
|
+
:datahike/STransactions STransactions
|
|
167
|
+
:datahike/SPullOptions SPullOptions
|
|
168
|
+
:datahike/SQueryArgs SQueryArgs
|
|
169
|
+
:datahike/SWithArgs SWithArgs
|
|
170
|
+
:datahike/SIndexLookupArgs SIndexLookupArgs
|
|
171
|
+
:datahike/SIndexRangeArgs SIndexRangeArgs
|
|
172
|
+
:datahike/SSchema SSchema
|
|
173
|
+
:datahike/SMetrics SMetrics
|
|
174
|
+
:datahike/time-point? time-point?}))
|
|
175
|
+
|
|
176
|
+
;; =============================================================================
|
|
177
|
+
;; Type Mappings for Code Generation
|
|
178
|
+
;; =============================================================================
|
|
179
|
+
|
|
180
|
+
(def malli->java-type
|
|
181
|
+
"Malli schema to Java type mapping."
|
|
182
|
+
{:SConfig "Map<String, Object>"
|
|
183
|
+
:SConnection "datahike.connector.Connection"
|
|
184
|
+
:SDB "datahike.db.DB"
|
|
185
|
+
:SEId "Object"
|
|
186
|
+
:SDatom "datahike.datom.Datom"
|
|
187
|
+
:SDatoms "List<Datom>"
|
|
188
|
+
:STransactions "List<Object>"
|
|
189
|
+
:STransactionReport "Map<String, Object>"
|
|
190
|
+
:SSchema "Map<Object, Object>"
|
|
191
|
+
:SMetrics "Map<String, Object>"
|
|
192
|
+
:SPullOptions "Map<String, Object>"
|
|
193
|
+
:SQueryArgs "Map<String, Object>"
|
|
194
|
+
:SIndexLookupArgs "Map<String, Object>"
|
|
195
|
+
:SIndexRangeArgs "Map<String, Object>"
|
|
196
|
+
:SWithArgs "Map<String, Object>"
|
|
197
|
+
;; Primitives
|
|
198
|
+
:boolean "boolean"
|
|
199
|
+
:int "int"
|
|
200
|
+
:long "long"
|
|
201
|
+
:double "double"
|
|
202
|
+
:string "String"
|
|
203
|
+
:keyword "String"
|
|
204
|
+
:any "Object"
|
|
205
|
+
:nil "void"
|
|
206
|
+
;; Collections
|
|
207
|
+
:vector "List"
|
|
208
|
+
:map "Map"
|
|
209
|
+
:set "Set"
|
|
210
|
+
:sequential "List"})
|
|
211
|
+
|
|
212
|
+
(def malli->typescript-type
|
|
213
|
+
"Malli schema to TypeScript type mapping."
|
|
214
|
+
{:SConfig "DatabaseConfig"
|
|
215
|
+
:SConnection "Connection"
|
|
216
|
+
:SDB "Database"
|
|
217
|
+
:SEId "number | [string, any] | string"
|
|
218
|
+
:SDatom "Datom"
|
|
219
|
+
:SDatoms "Datom[]"
|
|
220
|
+
:STransactions "Transaction[]"
|
|
221
|
+
:STransactionReport "TransactionReport"
|
|
222
|
+
:SSchema "Record<string, any>"
|
|
223
|
+
:SMetrics "Metrics"
|
|
224
|
+
:SPullOptions "PullOptions"
|
|
225
|
+
:SQueryArgs "QueryArgs"
|
|
226
|
+
:SIndexLookupArgs "IndexLookupArgs"
|
|
227
|
+
:SIndexRangeArgs "IndexRangeArgs"
|
|
228
|
+
:SWithArgs "WithArgs"
|
|
229
|
+
;; Primitives
|
|
230
|
+
:boolean "boolean"
|
|
231
|
+
:int "number"
|
|
232
|
+
:long "number"
|
|
233
|
+
:double "number"
|
|
234
|
+
:string "string"
|
|
235
|
+
:keyword "string"
|
|
236
|
+
:any "any"
|
|
237
|
+
:nil "null"
|
|
238
|
+
;; Collections
|
|
239
|
+
:vector "Array<any>"
|
|
240
|
+
:map "Record<string, any>"
|
|
241
|
+
:set "Set<any>"
|
|
242
|
+
:sequential "Array<any>"})
|
|
243
|
+
|
|
244
|
+
;; =============================================================================
|
|
245
|
+
;; Validation Helpers
|
|
246
|
+
;; =============================================================================
|
|
247
|
+
|
|
248
|
+
(defn validate
|
|
249
|
+
"Validate data against schema."
|
|
250
|
+
[schema data]
|
|
251
|
+
(m/validate schema data))
|
|
252
|
+
|
|
253
|
+
(defn explain
|
|
254
|
+
"Explain validation errors."
|
|
255
|
+
[schema data]
|
|
256
|
+
(m/explain schema data))
|
|
257
|
+
|
|
258
|
+
(defn humanize
|
|
259
|
+
"Get human-readable validation errors."
|
|
260
|
+
[explanation]
|
|
261
|
+
(me/humanize explanation))
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
(ns datahike.api
|
|
2
|
+
"Public API for datahike. Expanded from api.specification."
|
|
3
|
+
(:refer-clojure :exclude [filter])
|
|
4
|
+
#?(:cljs (:require-macros [datahike.api :refer [emit-api]]))
|
|
5
|
+
(:require [datahike.connector :as dc]
|
|
6
|
+
[datahike.config :as config]
|
|
7
|
+
[datahike.api.specification :refer [api-specification malli-schema->argslist]]
|
|
8
|
+
[datahike.api.impl]
|
|
9
|
+
[datahike.writer :as dw]
|
|
10
|
+
#?(:clj [datahike.http.writer])
|
|
11
|
+
[datahike.writing :as writing]
|
|
12
|
+
[konserve.store]
|
|
13
|
+
[datahike.constants :as const]
|
|
14
|
+
[datahike.core :as dcore]
|
|
15
|
+
[datahike.pull-api :as dp]
|
|
16
|
+
[datahike.query :as dq]
|
|
17
|
+
[datahike.schema :as ds]
|
|
18
|
+
[datahike.tools :as dt]
|
|
19
|
+
[datahike.db :as db #?@(:cljs [:refer [HistoricalDB AsOfDB SinceDB FilteredDB]])]
|
|
20
|
+
[datahike.db.interface :as dbi]
|
|
21
|
+
[datahike.db.transaction :as dbt]
|
|
22
|
+
[datahike.impl.entity :as de])
|
|
23
|
+
#?(:clj
|
|
24
|
+
(:import [clojure.lang Keyword PersistentArrayMap]
|
|
25
|
+
[datahike.db HistoricalDB AsOfDB SinceDB FilteredDB]
|
|
26
|
+
[datahike.impl.entity Entity])))
|
|
27
|
+
|
|
28
|
+
(defmacro ^:private emit-api []
|
|
29
|
+
`(do
|
|
30
|
+
~@(reduce
|
|
31
|
+
(fn [acc [n {:keys [args doc impl]}]]
|
|
32
|
+
(conj acc
|
|
33
|
+
`(def
|
|
34
|
+
~(with-meta n
|
|
35
|
+
{:arglists `(malli-schema->argslist (quote ~args))
|
|
36
|
+
:doc doc})
|
|
37
|
+
~impl)))
|
|
38
|
+
()
|
|
39
|
+
(into (sorted-map) api-specification))))
|
|
40
|
+
|
|
41
|
+
(emit-api)
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
(ns ^:no-doc datahike.array
|
|
2
|
+
#?(:cljs (:require [goog.array]))
|
|
3
|
+
#?(:clj (:import [java.util Arrays])))
|
|
4
|
+
|
|
5
|
+
#?(:clj
|
|
6
|
+
nil
|
|
7
|
+
#_(defonce ^:private hh-node-ns
|
|
8
|
+
(try
|
|
9
|
+
(require 'hitchhiker.tree.node)
|
|
10
|
+
(find-ns 'hitchhiker.tree.node)
|
|
11
|
+
(catch Exception _ nil))))
|
|
12
|
+
|
|
13
|
+
#?(:clj
|
|
14
|
+
(defn java8? []
|
|
15
|
+
(try
|
|
16
|
+
(= 8 (Integer/parseInt (subs (System/getProperty "java.specification.version") 2)))
|
|
17
|
+
(catch Exception _
|
|
18
|
+
false))))
|
|
19
|
+
|
|
20
|
+
#?(:clj
|
|
21
|
+
;; meta data doesn't get expanded in macros :/
|
|
22
|
+
(defn array-compare [a b] (Arrays/compare ^bytes a ^bytes b)))
|
|
23
|
+
|
|
24
|
+
#?(:clj
|
|
25
|
+
(defmacro raw-array-compare [a b]
|
|
26
|
+
(if (java8?)
|
|
27
|
+
;; slow fallback for Java 8, but has same semantics
|
|
28
|
+
`(let [bl# (alength ~b)
|
|
29
|
+
al# (alength ~a)]
|
|
30
|
+
(loop [i# 1]
|
|
31
|
+
(cond (and (> i# bl#) (> i# al#))
|
|
32
|
+
0
|
|
33
|
+
|
|
34
|
+
(> i# bl#)
|
|
35
|
+
1 ;; b is a prefix of a
|
|
36
|
+
|
|
37
|
+
(> i# al#)
|
|
38
|
+
-1 ;; a is a prefix of b
|
|
39
|
+
|
|
40
|
+
:else
|
|
41
|
+
(let [ec# (compare (aget ~a (dec i#)) (aget ~b (dec i#)))]
|
|
42
|
+
(if (not (zero? ec#))
|
|
43
|
+
ec#
|
|
44
|
+
(recur (inc i#)))))))
|
|
45
|
+
`(array-compare ~a ~b))))
|
|
46
|
+
|
|
47
|
+
#?(:cljs
|
|
48
|
+
(defn bytes? [x]
|
|
49
|
+
(and (instance? js/ArrayBuffer (.-buffer x))
|
|
50
|
+
(number? (.-byteLength x)))))
|
|
51
|
+
|
|
52
|
+
(defn compare-arrays
|
|
53
|
+
"Compare two arrays a and b element-wise in ascending order. If one array is a
|
|
54
|
+
prefix of another then it comes first."
|
|
55
|
+
[a b]
|
|
56
|
+
#?(:cljs (goog.array/compare3 a b)
|
|
57
|
+
:clj
|
|
58
|
+
(if (not (and (bytes? a) (bytes? b)))
|
|
59
|
+
(try
|
|
60
|
+
(compare a b)
|
|
61
|
+
(catch ClassCastException _
|
|
62
|
+
#_(if hh-node-ns
|
|
63
|
+
(let [order-fn (ns-resolve hh-node-ns '-order-on-edn-types)]
|
|
64
|
+
(- (order-fn a) (order-fn b)))
|
|
65
|
+
;; Fallback when hitchhiker-tree not available
|
|
66
|
+
(compare (class a) (class b)))))
|
|
67
|
+
(raw-array-compare a b))))
|
|
68
|
+
|
|
69
|
+
(defn string-from-bytes
|
|
70
|
+
"Represents a byte array as a string. Two byte arrays are said to be equal iff their corresponding values after applying this function are equal. That way, we rely on the equality and hash code implementations of the String class to compare byte arrays."
|
|
71
|
+
[x]
|
|
72
|
+
#?(:cljs (.decode (js/TextDecoder. "utf8") x)
|
|
73
|
+
:clj
|
|
74
|
+
(let [n (alength x)
|
|
75
|
+
dst (char-array n)]
|
|
76
|
+
(dotimes [i n]
|
|
77
|
+
(aset dst i (char (aget x i))))
|
|
78
|
+
(String. dst))))
|
|
79
|
+
|
|
80
|
+
(defrecord WrappedBytes [string-repr])
|
|
81
|
+
|
|
82
|
+
(defn wrap-comparable
|
|
83
|
+
"This functions is such that `(a= x y)` is equivalent to `(= (wrap-comparable x) (wrap-comparable y))`. This lets us also use these semantics in hash-sets or as keys in maps."
|
|
84
|
+
[x]
|
|
85
|
+
(if (bytes? x)
|
|
86
|
+
(WrappedBytes. #?(:clj (string-from-bytes x)
|
|
87
|
+
:cljs x))
|
|
88
|
+
x))
|
|
89
|
+
|
|
90
|
+
(defn a=
|
|
91
|
+
"Extension of Clojure's equality to things we also want to treat like values,
|
|
92
|
+
e.g. certain array types."
|
|
93
|
+
[a b]
|
|
94
|
+
(or (= a b)
|
|
95
|
+
#?(:clj (and (bytes? a)
|
|
96
|
+
(bytes? b)
|
|
97
|
+
(zero? (compare-arrays a b)))
|
|
98
|
+
:cljs (or (identical? a b)
|
|
99
|
+
(zero? (compare-arrays a b))))))
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
(ns datahike.cli
|
|
2
|
+
(:gen-class)
|
|
3
|
+
(:require [clojure.java.io :as io]
|
|
4
|
+
[clojure.pprint :refer [pprint]]
|
|
5
|
+
[clojure.string :as str]
|
|
6
|
+
[clojure.tools.cli :refer [parse-opts]]
|
|
7
|
+
[datahike.api :as d]
|
|
8
|
+
[datahike.pod :refer [run-pod]]
|
|
9
|
+
[datahike.codegen.cli :as cli-gen]
|
|
10
|
+
[clojure.edn :as edn]
|
|
11
|
+
[jsonista.core :as j]
|
|
12
|
+
[datahike.json :as json]
|
|
13
|
+
[clj-cbor.core :as cbor]
|
|
14
|
+
[taoensso.timbre :as log])
|
|
15
|
+
(:import [java.util Date]))
|
|
16
|
+
|
|
17
|
+
;; This file is following https://github.com/clojure/tools.cli
|
|
18
|
+
|
|
19
|
+
(when-not (= "true" (System/getenv "BABASHKA_POD"))
|
|
20
|
+
(log/merge-config!
|
|
21
|
+
{:appenders {:println {:enabled? false} ;; leave a "paper trail"
|
|
22
|
+
:stderr {:doc "Always prints to *err*"
|
|
23
|
+
:enabled? true
|
|
24
|
+
:fn (fn log-to-stderr [{:keys [output_]}]
|
|
25
|
+
(binding [*out* *err*]
|
|
26
|
+
(println (force output_))))}}}))
|
|
27
|
+
|
|
28
|
+
(defn get-version []
|
|
29
|
+
(or
|
|
30
|
+
;; Try to read version from resource file (generated at build time)
|
|
31
|
+
(try
|
|
32
|
+
(some-> (io/resource "DATAHIKE_VERSION")
|
|
33
|
+
slurp
|
|
34
|
+
str/trim)
|
|
35
|
+
(catch Exception _ nil))
|
|
36
|
+
;; Fallback for development builds
|
|
37
|
+
"development build"))
|
|
38
|
+
|
|
39
|
+
(defn usage [options-summary]
|
|
40
|
+
(cli-gen/generate-help options-summary))
|
|
41
|
+
|
|
42
|
+
(defn error-msg [errors]
|
|
43
|
+
(str "The following errors occurred while parsing your command:\n\n"
|
|
44
|
+
(str/join \newline errors)))
|
|
45
|
+
|
|
46
|
+
(def cli-options
|
|
47
|
+
;; An option with a required argument
|
|
48
|
+
(let [formats #{:json :edn :pprint :cbor}]
|
|
49
|
+
[["-f" "--format FORMAT" "Output format for the result."
|
|
50
|
+
:default :edn
|
|
51
|
+
:parse-fn keyword
|
|
52
|
+
:validate [formats (str "Must be one of: " (str/join ", " formats))]]
|
|
53
|
+
["-if" "--input-format FORMAT" "Input format for the transaction."
|
|
54
|
+
:default :edn
|
|
55
|
+
:parse-fn keyword
|
|
56
|
+
:validate [formats (str "Must be one of: " (str/join ", " formats))]]
|
|
57
|
+
[nil "--tx-file PATH" "Use this input file for transactions instead of command line or STDIN."
|
|
58
|
+
:default nil
|
|
59
|
+
:validate [#(.exists (io/file %)) "Transaction file does not exist."]]
|
|
60
|
+
;; A non-idempotent option (:default is applied first)
|
|
61
|
+
["-v" nil "Verbosity level"
|
|
62
|
+
:id :verbosity
|
|
63
|
+
:default 0
|
|
64
|
+
:update-fn inc]
|
|
65
|
+
;; A boolean option defaulting to nil
|
|
66
|
+
["-h" "--help"]
|
|
67
|
+
[nil "--version" "Show version"]]))
|
|
68
|
+
|
|
69
|
+
(defn validate-args
|
|
70
|
+
"Validate command line arguments. Either return a map indicating the program
|
|
71
|
+
should exit (with a error message, and optional ok status), or a map
|
|
72
|
+
indicating the command the program should execute and the options provided."
|
|
73
|
+
[args]
|
|
74
|
+
(let [{:keys [options arguments errors summary]} (parse-opts args cli-options)
|
|
75
|
+
pod? (= "true" (System/getenv "BABASHKA_POD"))
|
|
76
|
+
command-index (cli-gen/build-command-index)]
|
|
77
|
+
(cond
|
|
78
|
+
pod?
|
|
79
|
+
{:action :pod :options options}
|
|
80
|
+
|
|
81
|
+
(:help options) ; help => exit OK with usage summary
|
|
82
|
+
{:exit-message (usage summary) :ok? true :options options}
|
|
83
|
+
|
|
84
|
+
(:version options) ; version => exit OK with version string
|
|
85
|
+
{:exit-message (str "Datahike " (get-version)) :ok? true :options options}
|
|
86
|
+
|
|
87
|
+
errors ; errors => exit with description of errors
|
|
88
|
+
{:exit-message (error-msg errors) :options options}
|
|
89
|
+
|
|
90
|
+
;; Try to match command (could be multi-part like "db exists")
|
|
91
|
+
(seq arguments)
|
|
92
|
+
(let [;; Try 2-part command first (e.g., "db exists")
|
|
93
|
+
two-part (str/join " " (take 2 arguments))
|
|
94
|
+
one-part (first arguments)
|
|
95
|
+
|
|
96
|
+
[cmd-str remaining-args]
|
|
97
|
+
(cond
|
|
98
|
+
(get command-index two-part)
|
|
99
|
+
[two-part (drop 2 arguments)]
|
|
100
|
+
|
|
101
|
+
(get command-index one-part)
|
|
102
|
+
[one-part (rest arguments)]
|
|
103
|
+
|
|
104
|
+
:else
|
|
105
|
+
[nil arguments])]
|
|
106
|
+
|
|
107
|
+
(if cmd-str
|
|
108
|
+
{:command cmd-str
|
|
109
|
+
:options options
|
|
110
|
+
:arguments (vec remaining-args)}
|
|
111
|
+
{:exit-message (str "Unknown command: " (str/join " " (take 2 arguments))
|
|
112
|
+
"\n\nUse 'dthk --help' to see available commands.")
|
|
113
|
+
:options options}))
|
|
114
|
+
|
|
115
|
+
:else ; No arguments
|
|
116
|
+
{:exit-message (usage summary)
|
|
117
|
+
:options options})))
|
|
118
|
+
|
|
119
|
+
(defn exit [status msg]
|
|
120
|
+
(println msg)
|
|
121
|
+
(System/exit status))
|
|
122
|
+
|
|
123
|
+
(defn report [format out]
|
|
124
|
+
(case format
|
|
125
|
+
:json (println (j/write-value-as-string out))
|
|
126
|
+
:edn (println (pr-str out))
|
|
127
|
+
:pprint (pprint out)
|
|
128
|
+
:cbor (.write System/out ^bytes (cbor/encode out))))
|
|
129
|
+
|
|
130
|
+
(defn -main [& args]
|
|
131
|
+
(let [{:keys [action command options arguments exit-message ok?]}
|
|
132
|
+
(validate-args args)]
|
|
133
|
+
;; Set logging level
|
|
134
|
+
(case (int (:verbosity options))
|
|
135
|
+
0 (log/set-level! :warn)
|
|
136
|
+
1 (log/set-level! :info)
|
|
137
|
+
2 (log/set-level! :debug)
|
|
138
|
+
3 (log/set-level! :trace)
|
|
139
|
+
(exit 1 (str "Verbosity level not supported: " (:verbosity options))))
|
|
140
|
+
|
|
141
|
+
(cond
|
|
142
|
+
;; Exit with message
|
|
143
|
+
exit-message
|
|
144
|
+
(exit (if ok? 0 1) exit-message)
|
|
145
|
+
|
|
146
|
+
;; Babashka pod mode
|
|
147
|
+
(= action :pod)
|
|
148
|
+
(run-pod args)
|
|
149
|
+
|
|
150
|
+
;; Execute command via generated dispatch
|
|
151
|
+
command
|
|
152
|
+
(try
|
|
153
|
+
(let [result (cli-gen/dispatch-command
|
|
154
|
+
(str/split command #"\s+")
|
|
155
|
+
arguments
|
|
156
|
+
options)]
|
|
157
|
+
(report (:format options) result))
|
|
158
|
+
(catch Exception e
|
|
159
|
+
(println "❌ Error executing command:" (.getMessage e))
|
|
160
|
+
(when (>= (:verbosity options) 2)
|
|
161
|
+
(.printStackTrace e))
|
|
162
|
+
(exit 1 "")))
|
|
163
|
+
|
|
164
|
+
;; No command provided
|
|
165
|
+
:else
|
|
166
|
+
(exit 1 (usage (:summary (parse-opts args cli-options)))))))
|