@reddb-io/cli 1.2.5 → 1.4.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/README.md +27 -0
- package/drivers/js/package.json +2 -2
- package/drivers/js/src/db-helpers.js +1 -1
- package/drivers/js/src/documents.js +6 -2
- package/drivers/js/src/index.js +117 -0
- package/drivers/js/src/kv.js +8 -1
- package/drivers/js/src/queue.js +8 -0
- package/package.json +4 -1
package/README.md
CHANGED
|
@@ -635,3 +635,30 @@ RedDB from another Rust project.
|
|
|
635
635
|
---
|
|
636
636
|
|
|
637
637
|
**AGPL-3.0 License** -- Built by [RedDB.io](https://github.com/reddb-io)
|
|
638
|
+
|
|
639
|
+
<!-- contract-matrix:begin -->
|
|
640
|
+
## Public-surface support
|
|
641
|
+
|
|
642
|
+
> Generated from [`docs/conformance/public-surface-contract-matrix.json`](/docs/conformance/public-surface-contract-matrix.json) by `scripts/gen-docs-from-matrix.mjs`. Do not edit between the markers by hand — run `node scripts/gen-docs-from-matrix.mjs --write`. The matrix is the source of truth; this block can never claim more than it, and CI (`docs-matrix`) fails on drift.
|
|
643
|
+
>
|
|
644
|
+
> Every public RedDB promise and the status of each public surface that offers it.
|
|
645
|
+
|
|
646
|
+
| Promise | sql | http | redwire | grpc | driver_helpers |
|
|
647
|
+
| --- | --- | --- | --- | --- | --- |
|
|
648
|
+
| **PSC-001** — RedDB is one multi-model database (tables, graph, KV, timeseries, probabilistic, vector, queue, documents) backed by a single file. | ✅ supported | ✅ supported | ✅ supported | ✅ supported | ✅ supported |
|
|
649
|
+
| **PSC-002** — MATCH supports node, edge, label, property, and LIMIT projections. | ✅ supported | ✅ supported | ⚠️ partial | ⚠️ partial | ✅ supported |
|
|
650
|
+
| **PSC-003** — GRAPH algorithms accept semantic identifiers, limits, ordering, and return stable rich rows. | ✅ supported | ✅ supported | ❌ unsupported | ❌ unsupported | ❌ unsupported |
|
|
651
|
+
| **PSC-004** — INSERT creates rows, documents, and native timeseries points. | ✅ supported | ✅ supported | ⚠️ partial | ✅ supported | ✅ supported |
|
|
652
|
+
| **PSC-005** — HLL/SKETCH/FILTER expose write and read commands for cardinality, frequency, and membership. | ⚠️ partial | ❌ unsupported | ❌ unsupported | ❌ unsupported | ⚠️ partial |
|
|
653
|
+
| **PSC-006** — Timeseries stores timestamped metrics with tags and supports query/readback. | ✅ supported | ⚠️ partial | ❌ unsupported | ❌ unsupported | ⚠️ partial |
|
|
654
|
+
| **PSC-007** — Documents are first-class: create, read, update, delete, and SQL analytics over JSON. | ✅ supported | ✅ supported | ❌ unsupported | ✅ supported | ✅ supported |
|
|
655
|
+
| **PSC-008** — KV helpers expose get/put/delete; get of a missing key returns null, delete reports affected. | ✅ supported | ❌ unsupported | ❌ unsupported | ✅ supported | ✅ supported |
|
|
656
|
+
| **PSC-009** — Queue helpers expose create/push/peek/pop/len/purge with FIFO semantics; empty pop is not an error. | ✅ supported | ❌ unsupported | ❌ unsupported | ❌ unsupported | ✅ supported |
|
|
657
|
+
| **PSC-010** — Transactions are imperative (begin/commit/rollback) plus a run(callback) form; empty SQL rejects with INVALID_ARGUMENT. | ✅ supported | ❌ unsupported | ❌ unsupported | ✅ supported | ✅ supported |
|
|
658
|
+
| **PSC-011** — SQL aggregate, projection, expression, and mutation behaviour matches ordinary SQL expectations where advertised. | ✅ supported | ✅ supported | ⚠️ partial | ⚠️ partial | ✅ supported |
|
|
659
|
+
| **PSC-012** — Server transports expose the same query contract as embedded (HTTP, RedWire, gRPC parity). | ✅ supported | ✅ supported | ✅ supported | ✅ supported | ✅ supported |
|
|
660
|
+
| **PSC-013** — Official drivers implement the SDK Helper Spec v1.0 conformance suite (all 22 §12 case IDs). | ❌ unsupported | ❌ unsupported | ❌ unsupported | ✅ supported | ✅ supported |
|
|
661
|
+
| **PSC-014** — ASK / SEARCH semantic surfaces return ranked results with stable shape. | ✅ supported | ⚠️ partial | ❌ unsupported | ❌ unsupported | ⚠️ partial |
|
|
662
|
+
|
|
663
|
+
_Status legend: ✅ supported · ⚠️ partial (known gaps) · ❌ unsupported._
|
|
664
|
+
<!-- contract-matrix:end -->
|
package/drivers/js/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reddb-io/sdk",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "Official embedded RedDB SDK — launches a local red binary over stdio JSON-RPC. Use @reddb-io/client for remote HTTP, gRPC, and RedWire.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.js",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
],
|
|
21
21
|
"scripts": {
|
|
22
22
|
"postinstall": "node postinstall.js",
|
|
23
|
-
"test": "node --test test/ask.test.mjs test/cache.test.mjs test/db-helpers.test.mjs test/embedded-only.test.mjs test/insert-ids.test.mjs test/kv.test.mjs test/params.test.mjs test/postinstall.test.mjs test/queue.test.mjs test/redwire.params.test.mjs test/transaction.test.mjs && node test/smoke.test.mjs"
|
|
23
|
+
"test": "node --test test/ask.test.mjs test/cache.test.mjs test/db-helpers.test.mjs test/embedded-only.test.mjs test/helpers.test.mjs test/insert-ids.test.mjs test/kv.test.mjs test/params.test.mjs test/postinstall.test.mjs test/queue.test.mjs test/redwire.params.test.mjs test/transaction.test.mjs && node test/smoke.test.mjs && node test/conformance.test.mjs && node test/readme-examples.test.mjs"
|
|
24
24
|
},
|
|
25
25
|
"engines": {
|
|
26
26
|
"node": ">=18"
|
|
@@ -50,7 +50,7 @@ export class TypedQueryBuilder {
|
|
|
50
50
|
? '*'
|
|
51
51
|
: this.columns.map(sqlIdentifierPath).join(', ')
|
|
52
52
|
const where = this.whereClauses.length > 0
|
|
53
|
-
? ` WHERE ${this.whereClauses.join(' AND ')}`
|
|
53
|
+
? ` WHERE ${this.whereClauses.map((clause) => `(${clause})`).join(' AND ')}`
|
|
54
54
|
: ''
|
|
55
55
|
const sql = `SELECT ${projection} FROM ${sqlIdentifierPath(this.collection)}${where}`
|
|
56
56
|
const result = this.params.length > 0
|
|
@@ -40,7 +40,10 @@ export class DocumentClient {
|
|
|
40
40
|
validateObject(patch, 'documents.patch patch')
|
|
41
41
|
const entries = Object.entries(patch)
|
|
42
42
|
if (entries.length === 0) {
|
|
43
|
-
|
|
43
|
+
throw new RedDBError(
|
|
44
|
+
'INVALID_ARGUMENT',
|
|
45
|
+
'documents.patch patch must be a non-empty object',
|
|
46
|
+
)
|
|
44
47
|
}
|
|
45
48
|
for (const [field] of entries) {
|
|
46
49
|
if (field.includes('/')) {
|
|
@@ -66,7 +69,8 @@ export class DocumentClient {
|
|
|
66
69
|
|
|
67
70
|
async delete(collection, rid) {
|
|
68
71
|
const result = await this.db.delete(collection, rid)
|
|
69
|
-
|
|
72
|
+
const affected = result.affected ?? 0
|
|
73
|
+
return { affected, deleted: affected > 0 }
|
|
70
74
|
}
|
|
71
75
|
|
|
72
76
|
async ensureCollection(collection) {
|
package/drivers/js/src/index.js
CHANGED
|
@@ -43,6 +43,13 @@ export { parseUri, deriveLoginUrl } from './url.js'
|
|
|
43
43
|
export const EMBEDDED_ONLY_MESSAGE =
|
|
44
44
|
'remote URIs are not supported in @reddb-io/sdk; install @reddb-io/client for grpc/http/red transports'
|
|
45
45
|
|
|
46
|
+
/**
|
|
47
|
+
* SDK Helper Spec version this driver implements. See
|
|
48
|
+
* `docs/spec/sdk-helpers.md` §14 — every official driver exposes this so
|
|
49
|
+
* cross-driver CI dashboards can assert against it.
|
|
50
|
+
*/
|
|
51
|
+
export const HELPER_SPEC_VERSION = '1.0'
|
|
52
|
+
|
|
46
53
|
const MIN_INSERT_ID_ENGINE_VERSION = '1.0.9'
|
|
47
54
|
const NESTED_TX_NOT_SUPPORTED = 'NESTED_TX_NOT_SUPPORTED'
|
|
48
55
|
|
|
@@ -304,6 +311,92 @@ class TransactionHandle {
|
|
|
304
311
|
}
|
|
305
312
|
}
|
|
306
313
|
|
|
314
|
+
/**
|
|
315
|
+
* Spec §7 transaction client. Returned by `db.tx()`. Exposes the imperative
|
|
316
|
+
* `begin` / `commit` / `rollback` trio (each resolves to a `QueryResult`) plus
|
|
317
|
+
* the optional `run(callback)` form. Transaction state is tracked on the parent
|
|
318
|
+
* `RedDB` so it serialises with `db.transaction()` and nested opens are
|
|
319
|
+
* rejected rather than silently interleaved.
|
|
320
|
+
*/
|
|
321
|
+
export class TxClient {
|
|
322
|
+
constructor(db) {
|
|
323
|
+
this.db = db
|
|
324
|
+
this.active = false
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
async begin() {
|
|
328
|
+
if (this.db.inTransaction) {
|
|
329
|
+
throw nestedTransactionError()
|
|
330
|
+
}
|
|
331
|
+
this.db.inTransaction = true
|
|
332
|
+
this.active = true
|
|
333
|
+
try {
|
|
334
|
+
return await this.db.query('BEGIN')
|
|
335
|
+
} catch (err) {
|
|
336
|
+
this.db.inTransaction = false
|
|
337
|
+
this.active = false
|
|
338
|
+
throw err
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
async commit() {
|
|
343
|
+
if (!this.active) {
|
|
344
|
+
throw new RedDBError('INVALID_ARGUMENT', 'tx.commit() called without an open transaction')
|
|
345
|
+
}
|
|
346
|
+
try {
|
|
347
|
+
return await this.db.query('COMMIT')
|
|
348
|
+
} finally {
|
|
349
|
+
this.active = false
|
|
350
|
+
this.db.inTransaction = false
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
async rollback() {
|
|
355
|
+
if (!this.active) {
|
|
356
|
+
throw new RedDBError('INVALID_ARGUMENT', 'tx.rollback() called without an open transaction')
|
|
357
|
+
}
|
|
358
|
+
try {
|
|
359
|
+
return await this.db.query('ROLLBACK')
|
|
360
|
+
} finally {
|
|
361
|
+
this.active = false
|
|
362
|
+
this.db.inTransaction = false
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* Callback form: commit on success, roll back and re-throw on failure.
|
|
368
|
+
* Nested `tx.run` rejects with `INVALID_ARGUMENT` — callers wanting
|
|
369
|
+
* savepoints issue them directly via `tx.query()` (spec §7.2; the README
|
|
370
|
+
* records this choice).
|
|
371
|
+
*/
|
|
372
|
+
async run(callback) {
|
|
373
|
+
if (typeof callback !== 'function') {
|
|
374
|
+
throw new TypeError('tx.run(callback) requires a function')
|
|
375
|
+
}
|
|
376
|
+
if (this.db.inTransaction) {
|
|
377
|
+
throw new RedDBError(
|
|
378
|
+
'INVALID_ARGUMENT',
|
|
379
|
+
'nested tx.run() is not supported; issue savepoints via tx.query() instead',
|
|
380
|
+
)
|
|
381
|
+
}
|
|
382
|
+
await this.begin()
|
|
383
|
+
try {
|
|
384
|
+
const result = await callback(new TransactionHandle(this.db))
|
|
385
|
+
await this.commit()
|
|
386
|
+
return result
|
|
387
|
+
} catch (err) {
|
|
388
|
+
if (this.active) {
|
|
389
|
+
try {
|
|
390
|
+
await this.rollback()
|
|
391
|
+
} catch (rollbackErr) {
|
|
392
|
+
attachRollbackError(err, rollbackErr)
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
throw err
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
|
|
307
400
|
export class RedDB {
|
|
308
401
|
/**
|
|
309
402
|
* @param {RpcClient} client
|
|
@@ -315,8 +408,12 @@ export class RedDB {
|
|
|
315
408
|
constructor(client, opts = {}) {
|
|
316
409
|
this.client = client
|
|
317
410
|
this.transport = opts.transport ?? null
|
|
411
|
+
this.helperSpecVersion = HELPER_SPEC_VERSION
|
|
318
412
|
this.cache = new CacheClient(client, this.transport)
|
|
319
413
|
this.queue = new QueueClient(client)
|
|
414
|
+
// Spec §6: the canonical namespace is the plural `queues`. `queue` is kept
|
|
415
|
+
// as a back-compat alias to the same handle.
|
|
416
|
+
this.queues = this.queue
|
|
320
417
|
this.documents = new DocumentClient(this)
|
|
321
418
|
const defaultKv = new KvClient(client)
|
|
322
419
|
this.kv = Object.assign((collection = 'kv_default') => new KvClient(client, collection), {
|
|
@@ -341,6 +438,13 @@ export class RedDB {
|
|
|
341
438
|
* Returns `{ statement, affected, columns, rows }`.
|
|
342
439
|
*/
|
|
343
440
|
query(sql, ...params) {
|
|
441
|
+
// Spec §3.1 / §2.5: empty SQL is a caller bug; reject locally before
|
|
442
|
+
// touching the wire.
|
|
443
|
+
if (typeof sql !== 'string' || sql.trim().length === 0) {
|
|
444
|
+
return Promise.reject(
|
|
445
|
+
new RedDBError('INVALID_ARGUMENT', 'query() requires a non-empty SQL string'),
|
|
446
|
+
)
|
|
447
|
+
}
|
|
344
448
|
const wireParams = normalizeQueryParams(params)
|
|
345
449
|
if (wireParams == null) {
|
|
346
450
|
return this.client.call('query', { sql }).then(normalizeResult)
|
|
@@ -361,10 +465,23 @@ export class RedDB {
|
|
|
361
465
|
|
|
362
466
|
/** Insert many rows in one call. Returns `{ affected, rids, ids }`; `ids` is a legacy alias. */
|
|
363
467
|
async bulkInsert(collection, payloads) {
|
|
468
|
+
// Spec §3.4: empty payloads is a no-op returning `{ affected: 0, rids: [] }`.
|
|
469
|
+
if (Array.isArray(payloads) && payloads.length === 0) {
|
|
470
|
+
return { affected: 0, rids: [], ids: [] }
|
|
471
|
+
}
|
|
364
472
|
const result = await this.client.call('bulk_insert', { collection, payloads })
|
|
365
473
|
return requireInsertIds(result, payloads.length)
|
|
366
474
|
}
|
|
367
475
|
|
|
476
|
+
/**
|
|
477
|
+
* Spec §7 transaction handle. `db.tx()` returns a {@link TxClient} exposing
|
|
478
|
+
* imperative `begin` / `commit` / `rollback` plus a `run(callback)` form.
|
|
479
|
+
* `db.transaction(callback)` remains as the original callback-only shortcut.
|
|
480
|
+
*/
|
|
481
|
+
tx() {
|
|
482
|
+
return new TxClient(this)
|
|
483
|
+
}
|
|
484
|
+
|
|
368
485
|
async transaction(callback) {
|
|
369
486
|
if (this.inTransaction) {
|
|
370
487
|
throw nestedTransactionError()
|
package/drivers/js/src/kv.js
CHANGED
|
@@ -17,6 +17,11 @@ export class KvClient {
|
|
|
17
17
|
})
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
// Spec-canonical alias for `put` (SDK Helper Spec §5.1 `kv.set`).
|
|
21
|
+
set(key, value, options = {}) {
|
|
22
|
+
return this.put(key, value, options)
|
|
23
|
+
}
|
|
24
|
+
|
|
20
25
|
async get(key, options = {}) {
|
|
21
26
|
const collection = options.collection ?? this.collection
|
|
22
27
|
const result = await this.client.call('query', {
|
|
@@ -40,7 +45,9 @@ export class KvClient {
|
|
|
40
45
|
const result = await this.client.call('query', {
|
|
41
46
|
sql: `KV DELETE ${kvPath(collection, key)}`,
|
|
42
47
|
})
|
|
43
|
-
|
|
48
|
+
const affected = result.affected ?? result.affected_rows ?? 0
|
|
49
|
+
// Spec §5.4 / §2.4 DeleteResult: `deleted` is `affected > 0`.
|
|
50
|
+
return { affected, deleted: affected > 0 }
|
|
44
51
|
}
|
|
45
52
|
|
|
46
53
|
async list(options = {}) {
|
package/drivers/js/src/queue.js
CHANGED
|
@@ -5,6 +5,14 @@ export class QueueClient {
|
|
|
5
5
|
this.client = client
|
|
6
6
|
}
|
|
7
7
|
|
|
8
|
+
// Spec §6.1 `queues.create`: idempotent (CREATE QUEUE IF NOT EXISTS) so
|
|
9
|
+
// conformance fixtures can prime a queue the same way the Rust/Go harnesses do.
|
|
10
|
+
create(queue) {
|
|
11
|
+
return this.client.call('query', {
|
|
12
|
+
sql: `CREATE QUEUE IF NOT EXISTS ${queueIdentifier(queue)}`,
|
|
13
|
+
})
|
|
14
|
+
}
|
|
15
|
+
|
|
8
16
|
push(queue, value, options = {}) {
|
|
9
17
|
const priority = options.priority != null ? ` PRIORITY ${queuePriority(options.priority)}` : ''
|
|
10
18
|
return this.client.call('query', {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reddb-io/cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "CLI launcher for RedDB. The JS/TS app driver is published as @reddb-io/sdk.",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"bin": {
|
|
@@ -37,6 +37,9 @@
|
|
|
37
37
|
"scripts": {
|
|
38
38
|
"postinstall": "node drivers/js/cli-postinstall.js",
|
|
39
39
|
"test": "node drivers/js/test/smoke.test.mjs",
|
|
40
|
+
"typecheck": "cargo check --workspace --locked",
|
|
41
|
+
"lint": "cargo fmt --all -- --check && cargo clippy --workspace --locked -- -D warnings",
|
|
42
|
+
"build": "cargo test --workspace --locked",
|
|
40
43
|
"changeset": "changeset",
|
|
41
44
|
"release:version": "changeset version && node scripts/sync-version.js",
|
|
42
45
|
"release:publish": "changeset publish",
|