datahike 0.7.1630
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/README.md +226 -0
- package/cljs.analyzer.api.js +427 -0
- package/cljs.analyzer.api.js.map +1 -0
- package/cljs.analyzer.impl.js +86 -0
- package/cljs.analyzer.impl.js.map +1 -0
- package/cljs.analyzer.impl.namespaces.js +219 -0
- package/cljs.analyzer.impl.namespaces.js.map +1 -0
- package/cljs.analyzer.js +7186 -0
- package/cljs.analyzer.js.map +1 -0
- package/cljs.analyzer.passes.and_or.js +141 -0
- package/cljs.analyzer.passes.and_or.js.map +1 -0
- package/cljs.analyzer.passes.js +104 -0
- package/cljs.analyzer.passes.js.map +1 -0
- package/cljs.cache.js +1145 -0
- package/cljs.cache.js.map +1 -0
- package/cljs.cache.wrapped.js +352 -0
- package/cljs.cache.wrapped.js.map +1 -0
- package/cljs.core.async.impl.buffers.js +489 -0
- package/cljs.core.async.impl.buffers.js.map +1 -0
- package/cljs.core.async.impl.channels.js +711 -0
- package/cljs.core.async.impl.channels.js.map +1 -0
- package/cljs.core.async.impl.dispatch.js +81 -0
- package/cljs.core.async.impl.dispatch.js.map +1 -0
- package/cljs.core.async.impl.ioc_helpers.js +192 -0
- package/cljs.core.async.impl.ioc_helpers.js.map +1 -0
- package/cljs.core.async.impl.protocols.js +368 -0
- package/cljs.core.async.impl.protocols.js.map +1 -0
- package/cljs.core.async.impl.timers.js +510 -0
- package/cljs.core.async.impl.timers.js.map +1 -0
- package/cljs.core.async.js +8813 -0
- package/cljs.core.async.js.map +1 -0
- package/cljs.core.js +38764 -0
- package/cljs.core.js.map +1 -0
- package/cljs.env.js +60 -0
- package/cljs.env.js.map +1 -0
- package/cljs.nodejs.js +80 -0
- package/cljs.nodejs.js.map +1 -0
- package/cljs.reader.js +462 -0
- package/cljs.reader.js.map +1 -0
- package/cljs.spec.alpha.js +5312 -0
- package/cljs.spec.alpha.js.map +1 -0
- package/cljs.spec.gen.alpha.js +2020 -0
- package/cljs.spec.gen.alpha.js.map +1 -0
- package/cljs.tagged_literals.js +103 -0
- package/cljs.tagged_literals.js.map +1 -0
- package/cljs.tools.reader.edn.js +960 -0
- package/cljs.tools.reader.edn.js.map +1 -0
- package/cljs.tools.reader.impl.commons.js +217 -0
- package/cljs.tools.reader.impl.commons.js.map +1 -0
- package/cljs.tools.reader.impl.errors.js +441 -0
- package/cljs.tools.reader.impl.errors.js.map +1 -0
- package/cljs.tools.reader.impl.inspect.js +182 -0
- package/cljs.tools.reader.impl.inspect.js.map +1 -0
- package/cljs.tools.reader.impl.utils.js +429 -0
- package/cljs.tools.reader.impl.utils.js.map +1 -0
- package/cljs.tools.reader.js +1830 -0
- package/cljs.tools.reader.js.map +1 -0
- package/cljs.tools.reader.reader_types.js +838 -0
- package/cljs.tools.reader.reader_types.js.map +1 -0
- package/cljs_env.js +18192 -0
- package/cljs_node_io.core.js +1278 -0
- package/cljs_node_io.core.js.map +1 -0
- package/cljs_node_io.file.js +918 -0
- package/cljs_node_io.file.js.map +1 -0
- package/cljs_node_io.fs.js +3911 -0
- package/cljs_node_io.fs.js.map +1 -0
- package/clojure.data.js +319 -0
- package/clojure.data.js.map +1 -0
- package/clojure.edn.js +119 -0
- package/clojure.edn.js.map +1 -0
- package/clojure.set.js +406 -0
- package/clojure.set.js.map +1 -0
- package/clojure.string.js +502 -0
- package/clojure.string.js.map +1 -0
- package/clojure.walk.js +156 -0
- package/clojure.walk.js.map +1 -0
- package/datahike.api.impl.js +621 -0
- package/datahike.api.impl.js.map +1 -0
- package/datahike.api.specification.js +85 -0
- package/datahike.api.specification.js.map +1 -0
- package/datahike.array.js +275 -0
- package/datahike.array.js.map +1 -0
- package/datahike.config.js +437 -0
- package/datahike.config.js.map +1 -0
- package/datahike.connections.js +55 -0
- package/datahike.connections.js.map +1 -0
- package/datahike.connector.js +1248 -0
- package/datahike.connector.js.map +1 -0
- package/datahike.constants.js +66 -0
- package/datahike.constants.js.map +1 -0
- package/datahike.core.js +628 -0
- package/datahike.core.js.map +1 -0
- package/datahike.datom.js +963 -0
- package/datahike.datom.js.map +1 -0
- package/datahike.db.interface.js +819 -0
- package/datahike.db.interface.js.map +1 -0
- package/datahike.db.js +3095 -0
- package/datahike.db.js.map +1 -0
- package/datahike.db.search.js +1004 -0
- package/datahike.db.search.js.map +1 -0
- package/datahike.db.transaction.js +1904 -0
- package/datahike.db.transaction.js.map +1 -0
- package/datahike.db.utils.js +642 -0
- package/datahike.db.utils.js.map +1 -0
- package/datahike.gc.js +1618 -0
- package/datahike.gc.js.map +1 -0
- package/datahike.impl.entity.js +604 -0
- package/datahike.impl.entity.js.map +1 -0
- package/datahike.index.interface.js +484 -0
- package/datahike.index.interface.js.map +1 -0
- package/datahike.index.js +50 -0
- package/datahike.index.js.map +1 -0
- package/datahike.index.persistent_set.js +1250 -0
- package/datahike.index.persistent_set.js.map +1 -0
- package/datahike.js.api.js +2211 -0
- package/datahike.js.api.js.map +1 -0
- package/datahike.lru.js +404 -0
- package/datahike.lru.js.map +1 -0
- package/datahike.pull_api.js +533 -0
- package/datahike.pull_api.js.map +1 -0
- package/datahike.query.js +11891 -0
- package/datahike.query.js.map +1 -0
- package/datahike.query_stats.js +152 -0
- package/datahike.query_stats.js.map +1 -0
- package/datahike.readers.js +166 -0
- package/datahike.readers.js.map +1 -0
- package/datahike.schema.js +251 -0
- package/datahike.schema.js.map +1 -0
- package/datahike.schema_cache.js +66 -0
- package/datahike.schema_cache.js.map +1 -0
- package/datahike.spec.js +70 -0
- package/datahike.spec.js.map +1 -0
- package/datahike.store.js +514 -0
- package/datahike.store.js.map +1 -0
- package/datahike.tools.js +436 -0
- package/datahike.tools.js.map +1 -0
- package/datahike.writer.js +3463 -0
- package/datahike.writer.js.map +1 -0
- package/datahike.writing.js +3168 -0
- package/datahike.writing.js.map +1 -0
- package/datalog.parser.impl.js +1421 -0
- package/datalog.parser.impl.js.map +1 -0
- package/datalog.parser.impl.proto.js +213 -0
- package/datalog.parser.impl.proto.js.map +1 -0
- package/datalog.parser.impl.util.js +117 -0
- package/datalog.parser.impl.util.js.map +1 -0
- package/datalog.parser.js +47 -0
- package/datalog.parser.js.map +1 -0
- package/datalog.parser.pull.js +2603 -0
- package/datalog.parser.pull.js.map +1 -0
- package/datalog.parser.type.js +7502 -0
- package/datalog.parser.type.js.map +1 -0
- package/datalog.parser.util.js +60 -0
- package/datalog.parser.util.js.map +1 -0
- package/environ.core.js +263 -0
- package/environ.core.js.map +1 -0
- package/fress.api.js +607 -0
- package/fress.api.js.map +1 -0
- package/fress.impl.adler32.js +435 -0
- package/fress.impl.adler32.js.map +1 -0
- package/fress.impl.bigint.js +185 -0
- package/fress.impl.bigint.js.map +1 -0
- package/fress.impl.buffer.js +1087 -0
- package/fress.impl.buffer.js.map +1 -0
- package/fress.impl.codes.js +105 -0
- package/fress.impl.codes.js.map +1 -0
- package/fress.impl.hopmap.js +603 -0
- package/fress.impl.hopmap.js.map +1 -0
- package/fress.impl.ranges.js +49 -0
- package/fress.impl.ranges.js.map +1 -0
- package/fress.impl.raw_input.js +837 -0
- package/fress.impl.raw_input.js.map +1 -0
- package/fress.impl.raw_output.js +653 -0
- package/fress.impl.raw_output.js.map +1 -0
- package/fress.impl.table.js +133 -0
- package/fress.impl.table.js.map +1 -0
- package/fress.reader.js +2312 -0
- package/fress.reader.js.map +1 -0
- package/fress.util.js +458 -0
- package/fress.util.js.map +1 -0
- package/fress.writer.js +2141 -0
- package/fress.writer.js.map +1 -0
- package/geheimnis.aes.js +132 -0
- package/geheimnis.aes.js.map +1 -0
- package/hasch.base64.js +42 -0
- package/hasch.base64.js.map +1 -0
- package/hasch.benc.js +201 -0
- package/hasch.benc.js.map +1 -0
- package/hasch.core.js +188 -0
- package/hasch.core.js.map +1 -0
- package/hasch.platform.js +221 -0
- package/hasch.platform.js.map +1 -0
- package/incognito.base.js +276 -0
- package/incognito.base.js.map +1 -0
- package/incognito.edn.js +54 -0
- package/incognito.edn.js.map +1 -0
- package/incognito.fressian.js +330 -0
- package/incognito.fressian.js.map +1 -0
- package/index.d.ts +660 -0
- package/is.simm.partial_cps.async.js +142 -0
- package/is.simm.partial_cps.async.js.map +1 -0
- package/is.simm.partial_cps.runtime.js +65 -0
- package/is.simm.partial_cps.runtime.js.map +1 -0
- package/is.simm.partial_cps.sequence.js +1255 -0
- package/is.simm.partial_cps.sequence.js.map +1 -0
- package/konserve.cache.js +2519 -0
- package/konserve.cache.js.map +1 -0
- package/konserve.compressor.js +502 -0
- package/konserve.compressor.js.map +1 -0
- package/konserve.core.js +8052 -0
- package/konserve.core.js.map +1 -0
- package/konserve.encryptor.js +553 -0
- package/konserve.encryptor.js.map +1 -0
- package/konserve.gc.js +541 -0
- package/konserve.gc.js.map +1 -0
- package/konserve.impl.defaults.js +8290 -0
- package/konserve.impl.defaults.js.map +1 -0
- package/konserve.impl.storage_layout.js +849 -0
- package/konserve.impl.storage_layout.js.map +1 -0
- package/konserve.memory.js +2102 -0
- package/konserve.memory.js.map +1 -0
- package/konserve.node_filestore.js +2951 -0
- package/konserve.node_filestore.js.map +1 -0
- package/konserve.protocols.js +590 -0
- package/konserve.protocols.js.map +1 -0
- package/konserve.serializers.js +562 -0
- package/konserve.serializers.js.map +1 -0
- package/konserve.store.js +2245 -0
- package/konserve.store.js.map +1 -0
- package/konserve.tiered.js +9574 -0
- package/konserve.tiered.js.map +1 -0
- package/konserve.utils.js +180 -0
- package/konserve.utils.js.map +1 -0
- package/me.tonsky.persistent_sorted_set.arrays.js +66 -0
- package/me.tonsky.persistent_sorted_set.arrays.js.map +1 -0
- package/me.tonsky.persistent_sorted_set.branch.js +2779 -0
- package/me.tonsky.persistent_sorted_set.branch.js.map +1 -0
- package/me.tonsky.persistent_sorted_set.btset.js +14196 -0
- package/me.tonsky.persistent_sorted_set.btset.js.map +1 -0
- package/me.tonsky.persistent_sorted_set.impl.node.js +324 -0
- package/me.tonsky.persistent_sorted_set.impl.node.js.map +1 -0
- package/me.tonsky.persistent_sorted_set.impl.storage.js +123 -0
- package/me.tonsky.persistent_sorted_set.impl.storage.js.map +1 -0
- package/me.tonsky.persistent_sorted_set.js +890 -0
- package/me.tonsky.persistent_sorted_set.js.map +1 -0
- package/me.tonsky.persistent_sorted_set.leaf.js +561 -0
- package/me.tonsky.persistent_sorted_set.leaf.js.map +1 -0
- package/me.tonsky.persistent_sorted_set.util.js +402 -0
- package/me.tonsky.persistent_sorted_set.util.js.map +1 -0
- package/medley.core.js +1857 -0
- package/medley.core.js.map +1 -0
- package/package.json +38 -0
- package/spec_tools.core.js +1925 -0
- package/spec_tools.core.js.map +1 -0
- package/spec_tools.data_spec.js +1291 -0
- package/spec_tools.data_spec.js.map +1 -0
- package/spec_tools.form.js +185 -0
- package/spec_tools.form.js.map +1 -0
- package/spec_tools.impl.js +362 -0
- package/spec_tools.impl.js.map +1 -0
- package/spec_tools.parse.js +427 -0
- package/spec_tools.parse.js.map +1 -0
- package/spec_tools.transform.js +288 -0
- package/spec_tools.transform.js.map +1 -0
- package/superv.async.js +8617 -0
- package/superv.async.js.map +1 -0
- package/tailrecursion.priority_map.js +994 -0
- package/tailrecursion.priority_map.js.map +1 -0
- package/taoensso.encore.js +12385 -0
- package/taoensso.encore.js.map +1 -0
- package/taoensso.timbre.appenders.core.js +239 -0
- package/taoensso.timbre.appenders.core.js.map +1 -0
- package/taoensso.timbre.js +1264 -0
- package/taoensso.timbre.js.map +1 -0
- package/taoensso.truss.impl.js +442 -0
- package/taoensso.truss.impl.js.map +1 -0
- package/taoensso.truss.js +743 -0
- package/taoensso.truss.js.map +1 -0
- package/test-config-keys.js +33 -0
- package/test-final.js +36 -0
- package/test-key-duplication.js +67 -0
- package/test.js +557 -0
package/test.js
ADDED
|
@@ -0,0 +1,557 @@
|
|
|
1
|
+
// Comprehensive test for reorganized JS API - covers all nodejs_test.cljs functionality
|
|
2
|
+
const d = require('./datahike.js.api.js');
|
|
3
|
+
const crypto = require('crypto');
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const os = require('os');
|
|
6
|
+
const path = require('path');
|
|
7
|
+
|
|
8
|
+
// Helper to generate UUID for store :id
|
|
9
|
+
function generateUUID() {
|
|
10
|
+
return crypto.randomUUID();
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// Helper to find entity ID by name
|
|
14
|
+
async function findEntityByName(db, name) {
|
|
15
|
+
const datoms = await d.datoms(db, ':eavt');
|
|
16
|
+
if (!datoms) return null;
|
|
17
|
+
|
|
18
|
+
// Find datom where attribute is :name and value matches
|
|
19
|
+
for (const datom of datoms) {
|
|
20
|
+
if (datom.a && datom.a.fqn === 'name' && datom.v === name) {
|
|
21
|
+
return datom.e;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Helper to create temp directory
|
|
28
|
+
function tmpDir() {
|
|
29
|
+
return path.join(os.tmpdir(), `datahike-js-test-${Date.now()}-${Math.floor(Math.random() * 10000)}`);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async function testBasicOperations() {
|
|
33
|
+
console.log('\n=== Test 1: Basic Database Operations ===');
|
|
34
|
+
|
|
35
|
+
const config = {
|
|
36
|
+
store: { backend: ':memory', id: generateUUID() }
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
console.log(' Creating database...');
|
|
40
|
+
await d.createDatabase(config);
|
|
41
|
+
|
|
42
|
+
console.log(' Checking existence...');
|
|
43
|
+
const exists = await d.databaseExists(config);
|
|
44
|
+
if (!exists) throw new Error('Database should exist');
|
|
45
|
+
console.log(' ✓ Database exists');
|
|
46
|
+
|
|
47
|
+
console.log(' Connecting...');
|
|
48
|
+
const conn = await d.connect(config);
|
|
49
|
+
console.log(' ✓ Connected');
|
|
50
|
+
|
|
51
|
+
console.log(' Getting DB value...');
|
|
52
|
+
const db = await d.db(conn);
|
|
53
|
+
if (!db) throw new Error('DB value should exist');
|
|
54
|
+
console.log(' ✓ DB value retrieved');
|
|
55
|
+
|
|
56
|
+
d.release(conn);
|
|
57
|
+
await d.deleteDatabase(config);
|
|
58
|
+
|
|
59
|
+
const existsAfter = await d.databaseExists(config);
|
|
60
|
+
if (existsAfter) throw new Error('Database should not exist after deletion');
|
|
61
|
+
console.log(' ✓ Database deleted');
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async function testSchemaAndTransactions() {
|
|
65
|
+
console.log('\n=== Test 2: Schema and Transactions ===');
|
|
66
|
+
|
|
67
|
+
const config = {
|
|
68
|
+
store: { backend: ':memory', id: generateUUID() }
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
await d.createDatabase(config);
|
|
72
|
+
const conn = await d.connect(config);
|
|
73
|
+
|
|
74
|
+
console.log(' Transacting schema...');
|
|
75
|
+
const schema = [
|
|
76
|
+
{
|
|
77
|
+
'db/ident': ':name',
|
|
78
|
+
'db/valueType': ':db.type/string',
|
|
79
|
+
'db/cardinality': ':db.cardinality/one'
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
'db/ident': ':age',
|
|
83
|
+
'db/valueType': ':db.type/long',
|
|
84
|
+
'db/cardinality': ':db.cardinality/one'
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
'db/ident': ':email',
|
|
88
|
+
'db/valueType': ':db.type/string',
|
|
89
|
+
'db/cardinality': ':db.cardinality/one'
|
|
90
|
+
}
|
|
91
|
+
];
|
|
92
|
+
|
|
93
|
+
const schemaTx = await d.transact(conn, schema);
|
|
94
|
+
if (!schemaTx['tx-data'] || schemaTx['tx-data'].length === 0) {
|
|
95
|
+
throw new Error('Schema transaction should have datoms');
|
|
96
|
+
}
|
|
97
|
+
console.log(` ✓ Schema transacted (${schemaTx['tx-data'].length} datoms)`);
|
|
98
|
+
|
|
99
|
+
console.log(' Transacting data...');
|
|
100
|
+
const data = [
|
|
101
|
+
{ name: 'Alice', age: 30, email: 'alice@example.com' },
|
|
102
|
+
{ name: 'Bob', age: 25, email: 'bob@example.com' },
|
|
103
|
+
{ name: 'Charlie', age: 35, email: 'charlie@example.com' }
|
|
104
|
+
];
|
|
105
|
+
|
|
106
|
+
const dataTx = await d.transact(conn, data);
|
|
107
|
+
console.log(` ✓ Data transacted (${dataTx['tx-data'].length} datoms)`);
|
|
108
|
+
|
|
109
|
+
d.release(conn);
|
|
110
|
+
await d.deleteDatabase(config);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
async function testDatomsAPI() {
|
|
114
|
+
console.log('\n=== Test 3: Datoms API ===');
|
|
115
|
+
|
|
116
|
+
const config = {
|
|
117
|
+
store: { backend: ':memory', id: generateUUID() }
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
await d.createDatabase(config);
|
|
121
|
+
const conn = await d.connect(config);
|
|
122
|
+
|
|
123
|
+
const schema = [
|
|
124
|
+
{ 'db/ident': ':name', 'db/valueType': ':db.type/string', 'db/cardinality': ':db.cardinality/one' },
|
|
125
|
+
{ 'db/ident': ':age', 'db/valueType': ':db.type/long', 'db/cardinality': ':db.cardinality/one' }
|
|
126
|
+
];
|
|
127
|
+
await d.transact(conn, schema);
|
|
128
|
+
|
|
129
|
+
const data = [
|
|
130
|
+
{ name: 'Alice', age: 30 },
|
|
131
|
+
{ name: 'Bob', age: 25 }
|
|
132
|
+
];
|
|
133
|
+
await d.transact(conn, data);
|
|
134
|
+
|
|
135
|
+
console.log(' Getting datoms from EAVT index...');
|
|
136
|
+
const db = await d.db(conn);
|
|
137
|
+
const eavtDatoms = await d.datoms(db, ':eavt');
|
|
138
|
+
console.log(` ✓ EAVT datoms: ${eavtDatoms.length}`);
|
|
139
|
+
|
|
140
|
+
console.log(' Getting datoms from AVET index...');
|
|
141
|
+
const avetDatoms = await d.datoms(db, ':avet');
|
|
142
|
+
console.log(` ✓ AVET datoms: ${avetDatoms.length}`);
|
|
143
|
+
|
|
144
|
+
console.log(' Finding name datoms from EAVT...');
|
|
145
|
+
const eavtForNames = await d.datoms(db, ':eavt');
|
|
146
|
+
const nameDatoms = eavtForNames.filter(d => d.a && d.a.fqn === 'name');
|
|
147
|
+
if (nameDatoms.length !== 2) {
|
|
148
|
+
throw new Error(`Expected 2 name datoms, got ${nameDatoms.length}`);
|
|
149
|
+
}
|
|
150
|
+
console.log(` ✓ Name datoms: ${nameDatoms.length}`);
|
|
151
|
+
|
|
152
|
+
d.release(conn);
|
|
153
|
+
await d.deleteDatabase(config);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
async function testPullAPI() {
|
|
157
|
+
console.log('\n=== Test 4: Pull API ===');
|
|
158
|
+
|
|
159
|
+
const config = {
|
|
160
|
+
store: { backend: ':memory', id: generateUUID() }
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
await d.createDatabase(config);
|
|
164
|
+
const conn = await d.connect(config);
|
|
165
|
+
|
|
166
|
+
const schema = [
|
|
167
|
+
{ 'db/ident': ':name', 'db/valueType': ':db.type/string', 'db/cardinality': ':db.cardinality/one' },
|
|
168
|
+
{ 'db/ident': ':age', 'db/valueType': ':db.type/long', 'db/cardinality': ':db.cardinality/one' },
|
|
169
|
+
{ 'db/ident': ':email', 'db/valueType': ':db.type/string', 'db/cardinality': ':db.cardinality/one' }
|
|
170
|
+
];
|
|
171
|
+
await d.transact(conn, schema);
|
|
172
|
+
|
|
173
|
+
const data = [
|
|
174
|
+
{ name: 'Alice', age: 30, email: 'alice@example.com' }
|
|
175
|
+
];
|
|
176
|
+
await d.transact(conn, data);
|
|
177
|
+
|
|
178
|
+
console.log(' Finding entity ID...');
|
|
179
|
+
const db = await d.db(conn);
|
|
180
|
+
const eid = await findEntityByName(db, 'Alice');
|
|
181
|
+
if (!eid) throw new Error('Entity not found');
|
|
182
|
+
console.log(` ✓ Found entity: ${eid}`);
|
|
183
|
+
|
|
184
|
+
console.log(' Pulling entity with pattern...');
|
|
185
|
+
const pulled = await d.pull(db, [':name', ':age', ':email'], eid);
|
|
186
|
+
if (!pulled || !pulled.name || pulled.name !== 'Alice') {
|
|
187
|
+
throw new Error('Pull failed or incorrect data');
|
|
188
|
+
}
|
|
189
|
+
console.log(` ✓ Pulled: ${pulled.name}, age ${pulled.age}, email ${pulled.email}`);
|
|
190
|
+
|
|
191
|
+
console.log(' Pulling with wildcard...');
|
|
192
|
+
const pulledAll = await d.pull(db, ['*'], eid);
|
|
193
|
+
if (!pulledAll || !pulledAll.name) {
|
|
194
|
+
throw new Error('Pull with wildcard failed');
|
|
195
|
+
}
|
|
196
|
+
console.log(` ✓ Pulled with wildcard: ${Object.keys(pulledAll).length} keys`);
|
|
197
|
+
|
|
198
|
+
console.log(' Pull many...');
|
|
199
|
+
const pulledMany = await d.pullMany(db, [':name'], [eid]);
|
|
200
|
+
if (!pulledMany || pulledMany.length !== 1) {
|
|
201
|
+
throw new Error('Pull many failed');
|
|
202
|
+
}
|
|
203
|
+
console.log(` ✓ Pull many: ${pulledMany.length} entities`);
|
|
204
|
+
|
|
205
|
+
d.release(conn);
|
|
206
|
+
await d.deleteDatabase(config);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
async function testEntityAPI() {
|
|
210
|
+
console.log('\n=== Test 5: Entity API ===');
|
|
211
|
+
|
|
212
|
+
const config = {
|
|
213
|
+
store: { backend: ':memory', id: generateUUID() }
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
await d.createDatabase(config);
|
|
217
|
+
const conn = await d.connect(config);
|
|
218
|
+
|
|
219
|
+
const schema = [
|
|
220
|
+
{ 'db/ident': ':name', 'db/valueType': ':db.type/string', 'db/cardinality': ':db.cardinality/one' },
|
|
221
|
+
{ 'db/ident': ':age', 'db/valueType': ':db.type/long', 'db/cardinality': ':db.cardinality/one' },
|
|
222
|
+
{ 'db/ident': ':email', 'db/valueType': ':db.type/string', 'db/cardinality': ':db.cardinality/one' }
|
|
223
|
+
];
|
|
224
|
+
await d.transact(conn, schema);
|
|
225
|
+
|
|
226
|
+
const data = [
|
|
227
|
+
{ name: 'Bob', age: 25, email: 'bob@example.com' }
|
|
228
|
+
];
|
|
229
|
+
await d.transact(conn, data);
|
|
230
|
+
|
|
231
|
+
console.log(' Finding entity ID...');
|
|
232
|
+
const db = await d.db(conn);
|
|
233
|
+
const eid = await findEntityByName(db, 'Bob');
|
|
234
|
+
if (!eid) throw new Error('Entity not found');
|
|
235
|
+
console.log(` ✓ Found entity: ${eid}`);
|
|
236
|
+
|
|
237
|
+
console.log(' Getting entity...');
|
|
238
|
+
const entity = await d.entity(db, eid);
|
|
239
|
+
if (!entity) throw new Error('Entity retrieval failed');
|
|
240
|
+
console.log(` ✓ Entity retrieved (type: ${typeof entity})`);
|
|
241
|
+
|
|
242
|
+
// Note: Entity API returns a ClojureScript Entity object, not a plain JS object
|
|
243
|
+
// Use Pull API for getting entity data as plain JS object
|
|
244
|
+
console.log(' Using pull to get entity data...');
|
|
245
|
+
const pulled = await d.pull(db, [':name'], eid);
|
|
246
|
+
if (pulled.name !== 'Bob') {
|
|
247
|
+
throw new Error(`Expected name 'Bob' via pull, got '${pulled.name}'`);
|
|
248
|
+
}
|
|
249
|
+
console.log(` ✓ Entity has correct name via pull: ${pulled.name}`);
|
|
250
|
+
|
|
251
|
+
d.release(conn);
|
|
252
|
+
await d.deleteDatabase(config);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
async function testTemporalDatabases() {
|
|
256
|
+
console.log('\n=== Test 6: Temporal Databases (History, as-of) ===');
|
|
257
|
+
|
|
258
|
+
const config = {
|
|
259
|
+
store: { backend: ':memory', id: generateUUID() },
|
|
260
|
+
'keep-history?': true
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
await d.createDatabase(config);
|
|
264
|
+
const conn = await d.connect(config);
|
|
265
|
+
|
|
266
|
+
const schema = [
|
|
267
|
+
{ 'db/ident': ':value', 'db/valueType': ':db.type/long', 'db/cardinality': ':db.cardinality/one' }
|
|
268
|
+
];
|
|
269
|
+
await d.transact(conn, schema);
|
|
270
|
+
|
|
271
|
+
console.log(' Transacting initial value...');
|
|
272
|
+
await d.transact(conn, [{ value: 10 }]);
|
|
273
|
+
const db1 = await d.db(conn);
|
|
274
|
+
console.log(' ✓ Initial transaction complete');
|
|
275
|
+
|
|
276
|
+
console.log(' Transacting updated value...');
|
|
277
|
+
await d.transact(conn, [{ value: 20 }]);
|
|
278
|
+
const db2 = await d.db(conn);
|
|
279
|
+
console.log(' ✓ Updated transaction complete');
|
|
280
|
+
|
|
281
|
+
console.log(' Checking current datoms...');
|
|
282
|
+
const currentDatoms = await d.datoms(db2, ':eavt');
|
|
283
|
+
const valueDatoms2 = currentDatoms.filter(d => d.a && d.a.fqn === 'value');
|
|
284
|
+
const values2 = valueDatoms2.map(d => d.v);
|
|
285
|
+
console.log(` ✓ Current values in DB: ${values2.join(', ')}`);
|
|
286
|
+
|
|
287
|
+
console.log(' Using as-of to view old state...');
|
|
288
|
+
const dbAsOf = await d.asOf(db2, db1);
|
|
289
|
+
const asOfDatoms = await d.datoms(dbAsOf, ':eavt');
|
|
290
|
+
const valueDatoms1 = asOfDatoms.filter(d => d.a && d.a.fqn === 'value');
|
|
291
|
+
const values1 = valueDatoms1.map(d => d.v);
|
|
292
|
+
console.log(` ✓ as-of values: ${values1.join(', ')}`);
|
|
293
|
+
|
|
294
|
+
console.log(' Getting history database...');
|
|
295
|
+
const histDb = await d.history(db2);
|
|
296
|
+
const histDatoms = await d.datoms(histDb, ':eavt');
|
|
297
|
+
if (histDatoms.length < 2) {
|
|
298
|
+
throw new Error('History should have multiple datoms');
|
|
299
|
+
}
|
|
300
|
+
console.log(` ✓ History DB has ${histDatoms.length} datoms`);
|
|
301
|
+
|
|
302
|
+
d.release(conn);
|
|
303
|
+
await d.deleteDatabase(config);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
async function testFilePersistence() {
|
|
307
|
+
console.log('\n=== Test 7: File Backend Persistence ===');
|
|
308
|
+
|
|
309
|
+
const dir = tmpDir();
|
|
310
|
+
const config = {
|
|
311
|
+
store: { backend: ':file', path: dir, id: generateUUID() }
|
|
312
|
+
};
|
|
313
|
+
|
|
314
|
+
console.log(` Using temp directory: ${dir}`);
|
|
315
|
+
|
|
316
|
+
console.log(' Creating database and adding data...');
|
|
317
|
+
await d.createDatabase(config);
|
|
318
|
+
let conn = await d.connect(config);
|
|
319
|
+
|
|
320
|
+
const schema = [
|
|
321
|
+
{ 'db/ident': ':name', 'db/valueType': ':db.type/string', 'db/cardinality': ':db.cardinality/one' }
|
|
322
|
+
];
|
|
323
|
+
await d.transact(conn, schema);
|
|
324
|
+
|
|
325
|
+
const data = [{ name: 'Persisted Alice' }];
|
|
326
|
+
await d.transact(conn, data);
|
|
327
|
+
|
|
328
|
+
console.log(' ✓ Data transacted');
|
|
329
|
+
|
|
330
|
+
console.log(' Releasing connection...');
|
|
331
|
+
d.release(conn);
|
|
332
|
+
|
|
333
|
+
console.log(' Reconnecting to same database...');
|
|
334
|
+
conn = await d.connect(config);
|
|
335
|
+
const db = await d.db(conn);
|
|
336
|
+
|
|
337
|
+
console.log(' Checking persisted data...');
|
|
338
|
+
const eid = await findEntityByName(db, 'Persisted Alice');
|
|
339
|
+
if (!eid) throw new Error('Persisted data not found after reconnect');
|
|
340
|
+
console.log(' ✓ Data persisted across reconnection');
|
|
341
|
+
|
|
342
|
+
d.release(conn);
|
|
343
|
+
|
|
344
|
+
console.log(' Deleting database...');
|
|
345
|
+
await d.deleteDatabase(config);
|
|
346
|
+
|
|
347
|
+
if (fs.existsSync(dir)) {
|
|
348
|
+
throw new Error('Directory should be cleaned up');
|
|
349
|
+
}
|
|
350
|
+
console.log(' ✓ Database and directory cleaned up');
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
async function testSchemaRetrieval() {
|
|
354
|
+
console.log('\n=== Test 8: Schema Retrieval ===');
|
|
355
|
+
|
|
356
|
+
const config = {
|
|
357
|
+
store: { backend: ':memory', id: generateUUID() }
|
|
358
|
+
};
|
|
359
|
+
|
|
360
|
+
await d.createDatabase(config);
|
|
361
|
+
const conn = await d.connect(config);
|
|
362
|
+
|
|
363
|
+
const schema = [
|
|
364
|
+
{ 'db/ident': ':name', 'db/valueType': ':db.type/string', 'db/cardinality': ':db.cardinality/one' },
|
|
365
|
+
{ 'db/ident': ':age', 'db/valueType': ':db.type/long', 'db/cardinality': ':db.cardinality/one' }
|
|
366
|
+
];
|
|
367
|
+
await d.transact(conn, schema);
|
|
368
|
+
|
|
369
|
+
console.log(' Retrieving schema...');
|
|
370
|
+
const db = await d.db(conn);
|
|
371
|
+
const retrievedSchema = await d.schema(db);
|
|
372
|
+
|
|
373
|
+
if (!retrievedSchema || Object.keys(retrievedSchema).length === 0) {
|
|
374
|
+
throw new Error('Schema retrieval failed');
|
|
375
|
+
}
|
|
376
|
+
console.log(` ✓ Schema retrieved: ${Object.keys(retrievedSchema).length} attributes`);
|
|
377
|
+
|
|
378
|
+
d.release(conn);
|
|
379
|
+
await d.deleteDatabase(config);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
async function testMultipleTransactions() {
|
|
383
|
+
console.log('\n=== Test 9: Multiple Sequential Transactions ===');
|
|
384
|
+
|
|
385
|
+
const config = {
|
|
386
|
+
store: { backend: ':memory', id: generateUUID() }
|
|
387
|
+
};
|
|
388
|
+
|
|
389
|
+
await d.createDatabase(config);
|
|
390
|
+
const conn = await d.connect(config);
|
|
391
|
+
|
|
392
|
+
const schema = [
|
|
393
|
+
{ 'db/ident': ':counter', 'db/valueType': ':db.type/long', 'db/cardinality': ':db.cardinality/one' }
|
|
394
|
+
];
|
|
395
|
+
await d.transact(conn, schema);
|
|
396
|
+
|
|
397
|
+
console.log(' Transacting multiple values...');
|
|
398
|
+
for (let i = 1; i <= 5; i++) {
|
|
399
|
+
await d.transact(conn, [{ counter: i * 10 }]);
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
const db = await d.db(conn);
|
|
403
|
+
const datoms = await d.datoms(db, ':eavt');
|
|
404
|
+
const counterDatoms = datoms.filter(d => d.a && d.a.fqn === 'counter');
|
|
405
|
+
|
|
406
|
+
if (!counterDatoms || counterDatoms.length < 5) {
|
|
407
|
+
throw new Error(`Expected at least 5 counter values, got ${counterDatoms ? counterDatoms.length : 0}`);
|
|
408
|
+
}
|
|
409
|
+
console.log(` ✓ Multiple transactions completed: ${counterDatoms.length} counter datoms`);
|
|
410
|
+
|
|
411
|
+
d.release(conn);
|
|
412
|
+
await d.deleteDatabase(config);
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
async function testQueryAPI() {
|
|
416
|
+
console.log('\n=== Test 10: Query API (Datalog queries as EDN strings) ===');
|
|
417
|
+
|
|
418
|
+
const config = {
|
|
419
|
+
store: { backend: ':memory', id: generateUUID() }
|
|
420
|
+
};
|
|
421
|
+
|
|
422
|
+
await d.createDatabase(config);
|
|
423
|
+
const conn = await d.connect(config);
|
|
424
|
+
|
|
425
|
+
const schema = [
|
|
426
|
+
{ 'db/ident': ':name', 'db/valueType': ':db.type/string', 'db/cardinality': ':db.cardinality/one' },
|
|
427
|
+
{ 'db/ident': ':age', 'db/valueType': ':db.type/long', 'db/cardinality': ':db.cardinality/one' },
|
|
428
|
+
{ 'db/ident': ':email', 'db/valueType': ':db.type/string', 'db/cardinality': ':db.cardinality/one' }
|
|
429
|
+
];
|
|
430
|
+
await d.transact(conn, schema);
|
|
431
|
+
|
|
432
|
+
const data = [
|
|
433
|
+
{ name: 'Alice', age: 30, email: 'alice@example.com' },
|
|
434
|
+
{ name: 'Bob', age: 25, email: 'bob@example.com' },
|
|
435
|
+
{ name: 'Charlie', age: 35, email: 'charlie@example.com' }
|
|
436
|
+
];
|
|
437
|
+
await d.transact(conn, data);
|
|
438
|
+
|
|
439
|
+
const db = await d.db(conn);
|
|
440
|
+
|
|
441
|
+
console.log(' Testing find-rel (return tuples)...');
|
|
442
|
+
const q1 = await d.q('[:find ?e ?name :where [?e :name ?name]]', db);
|
|
443
|
+
if (!q1 || q1.length !== 3) {
|
|
444
|
+
throw new Error(`Expected 3 results, got ${q1 ? q1.length : 0}`);
|
|
445
|
+
}
|
|
446
|
+
console.log(` ✓ Find-rel query returned ${q1.length} tuples`);
|
|
447
|
+
|
|
448
|
+
console.log(' Testing find-coll (return single column)...');
|
|
449
|
+
const q2 = await d.q('[:find [?name ...] :where [_ :name ?name]]', db);
|
|
450
|
+
if (!q2 || q2.length !== 3) {
|
|
451
|
+
throw new Error(`Expected 3 names, got ${q2 ? q2.length : 0}`);
|
|
452
|
+
}
|
|
453
|
+
console.log(` ✓ Find-coll query returned ${q2.length} values`);
|
|
454
|
+
|
|
455
|
+
console.log(' Testing find-tuple (return single tuple)...');
|
|
456
|
+
const q3 = await d.q('[:find [?name ?age] :where [?e :name "Alice"] [?e :age ?age] [?e :name ?name]]', db);
|
|
457
|
+
if (!q3 || q3.length !== 2) {
|
|
458
|
+
throw new Error(`Expected tuple [name, age], got ${JSON.stringify(q3)}`);
|
|
459
|
+
}
|
|
460
|
+
if (q3[0] !== 'Alice' || q3[1] !== 30) {
|
|
461
|
+
throw new Error(`Expected ["Alice", 30], got ${JSON.stringify(q3)}`);
|
|
462
|
+
}
|
|
463
|
+
console.log(` ✓ Find-tuple query returned [${q3[0]}, ${q3[1]}]`);
|
|
464
|
+
|
|
465
|
+
console.log(' Testing find-scalar (return single value)...');
|
|
466
|
+
const q4 = await d.q('[:find ?name . :where [?e :name ?name] [?e :age 25]]', db);
|
|
467
|
+
if (q4 !== 'Bob') {
|
|
468
|
+
throw new Error(`Expected "Bob", got ${q4}`);
|
|
469
|
+
}
|
|
470
|
+
console.log(` ✓ Find-scalar query returned: ${q4}`);
|
|
471
|
+
|
|
472
|
+
console.log(' Testing query with multiple clauses...');
|
|
473
|
+
const q5 = await d.q('[:find ?name ?age :where [?e :name ?name] [?e :age ?age] [(> ?age 28)]]', db);
|
|
474
|
+
if (!q5 || q5.length !== 2) {
|
|
475
|
+
throw new Error(`Expected 2 results (age > 28), got ${q5 ? q5.length : 0}`);
|
|
476
|
+
}
|
|
477
|
+
console.log(` ✓ Multi-clause query returned ${q5.length} results`);
|
|
478
|
+
|
|
479
|
+
console.log(' Testing query with input parameter...');
|
|
480
|
+
const q6 = await d.q('[:find ?e :in $ ?name :where [?e :name ?name]]', db, 'Charlie');
|
|
481
|
+
if (!q6 || q6.length !== 1) {
|
|
482
|
+
throw new Error(`Expected 1 result for Charlie, got ${q6 ? q6.length : 0}`);
|
|
483
|
+
}
|
|
484
|
+
console.log(` ✓ Parameterized query found entity for Charlie`);
|
|
485
|
+
|
|
486
|
+
d.release(conn);
|
|
487
|
+
await d.deleteDatabase(config);
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
// Main test runner
|
|
491
|
+
async function runAllTests() {
|
|
492
|
+
console.log('╔════════════════════════════════════════════════════════╗');
|
|
493
|
+
console.log('║ Comprehensive Datahike JavaScript API Test Suite ║');
|
|
494
|
+
console.log('╚════════════════════════════════════════════════════════╝');
|
|
495
|
+
|
|
496
|
+
const startTime = Date.now();
|
|
497
|
+
let passed = 0;
|
|
498
|
+
let failed = 0;
|
|
499
|
+
|
|
500
|
+
const tests = [
|
|
501
|
+
testBasicOperations,
|
|
502
|
+
testSchemaAndTransactions,
|
|
503
|
+
testDatomsAPI,
|
|
504
|
+
testPullAPI,
|
|
505
|
+
testEntityAPI,
|
|
506
|
+
testTemporalDatabases,
|
|
507
|
+
testFilePersistence,
|
|
508
|
+
testSchemaRetrieval,
|
|
509
|
+
testMultipleTransactions,
|
|
510
|
+
testQueryAPI
|
|
511
|
+
];
|
|
512
|
+
|
|
513
|
+
for (const test of tests) {
|
|
514
|
+
try {
|
|
515
|
+
await test();
|
|
516
|
+
passed++;
|
|
517
|
+
} catch (err) {
|
|
518
|
+
failed++;
|
|
519
|
+
console.error(`\n❌ Test failed: ${err.message}`);
|
|
520
|
+
console.error(err.stack);
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
const duration = Date.now() - startTime;
|
|
525
|
+
|
|
526
|
+
console.log('\n╔════════════════════════════════════════════════════════╗');
|
|
527
|
+
console.log('║ Test Summary ║');
|
|
528
|
+
console.log('╚════════════════════════════════════════════════════════╝');
|
|
529
|
+
console.log(` Total: ${tests.length} tests`);
|
|
530
|
+
console.log(` Passed: ${passed} ✓`);
|
|
531
|
+
console.log(` Failed: ${failed} ✗`);
|
|
532
|
+
console.log(` Duration: ${duration}ms`);
|
|
533
|
+
console.log('');
|
|
534
|
+
|
|
535
|
+
if (failed === 0) {
|
|
536
|
+
console.log('🎉 All tests passed!');
|
|
537
|
+
} else {
|
|
538
|
+
console.log(`⚠️ ${failed} test(s) failed`);
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
return failed === 0;
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
// Run tests and exit with timeout
|
|
545
|
+
runAllTests()
|
|
546
|
+
.then(success => {
|
|
547
|
+
// Force exit after 2 seconds to handle any lingering event loop activity
|
|
548
|
+
setTimeout(() => {
|
|
549
|
+
process.exit(success ? 0 : 1);
|
|
550
|
+
}, 2000);
|
|
551
|
+
})
|
|
552
|
+
.catch(err => {
|
|
553
|
+
console.error('Fatal error:', err);
|
|
554
|
+
setTimeout(() => {
|
|
555
|
+
process.exit(1);
|
|
556
|
+
}, 2000);
|
|
557
|
+
});
|