@push.rocks/smartdb 5.3.2 → 5.5.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@push.rocks/smartdb",
3
- "version": "5.3.2",
3
+ "version": "5.5.0",
4
4
  "private": false,
5
5
  "description": "A MongoDB-compatible embedded database server with wire protocol support, backed by a high-performance Rust engine.",
6
6
  "exports": {
@@ -15,10 +15,10 @@
15
15
  "@api.global/typedserver": "^8.4.6",
16
16
  "@design.estate/dees-element": "^2.2.4",
17
17
  "@git.zone/tsbuild": "^4.4.2",
18
- "@git.zone/tsbundle": "^2.11.4",
19
- "@git.zone/tsrust": "^1.10.3",
18
+ "@git.zone/tsbundle": "^2.13.0",
19
+ "@git.zone/tsrust": "^1.10.4",
20
20
  "@git.zone/tstest": "^4.0.0",
21
- "@types/node": "26.2.0",
21
+ "@types/node": "26.4.1",
22
22
  "mongodb": "^7.5.0"
23
23
  },
24
24
  "dependencies": {
package/readme.hints.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # smartdb hints
2
2
 
3
+ ## Single-Node Concern Profile, Namespace Clock, and Deadlines (5.5.0)
4
+
5
+ - `rustdb-commands/src/concerns.rs` is the sole owner of `readConcern`, `writeConcern`, and `maxTimeMS` validation. It runs in the router before authentication, sessions, permits, or gates. The profile is stated for one voting node: fsynced acknowledgement satisfies `w: 1`, `w: "majority"`, and `j: true`; `w: N > 1` is `UnsatisfiableWriteConcern` (100), tag sets are `UnknownReplWriteConcern` (79), cluster-time read concerns and misplaced fields are `InvalidOptions` (72), malformed deadlines are `BadValue` (2). `j: true` on `MemoryStorageAdapter` is `BadValue` through `StorageAdapter::journaled_writes()`. The driver sends a transaction's read concern on whichever statement starts the transaction, so a transaction-starting insert may carry one. Never accept a concern by widening an allowlist; every accepted value must map to a guarantee the engine actually provides.
6
+ - `rustdb-commands/src/clock.rs` (`CommandContext::namespace_clock`) is the publication clock. Every in-process publication that changes visible namespace contents records `publish_namespace` while it still holds the namespace write lock (point writes, transaction commit completion, `create`, `drop`, `renameCollection`, `$out`, `$merge`, oplog revert) or `publish_database` (`dropDatabase`, in-process durable replacement through `invalidate_database_runtime`, and cross-process publication observed through `invalidate_database_runtime_for_epoch`). `$out` publishes right after it drops the old contents and again after the refill, so a failed refill still invalidates pinned snapshots. Index DDL does not change documents and does not publish. Adding a new mutation path without a publication is a snapshot-consistency bug.
7
+ - Transactions pin `TransactionState::snapshot_sequence` when they materialize their first namespace. `transactions::load_transaction_docs_locked` requires the caller to hold the namespace write lock (insert/update/delete already do); `load_transaction_docs` acquires it for readers (`find`, `count`, `distinct`). A namespace whose `last_publication` exceeds the pinned sequence fails with `SnapshotUnavailable` (246); the router labels it `TransientTransactionError` so `withTransaction` restarts. This turns lazy per-namespace materialization into one database-wide snapshot without a second data copy; Storage VNext MVCC read timestamps replace the materialized copy itself.
8
+ - Sessions retain `last_outcome` (`TransactionOutcome::Committed`/`Aborted` per transaction number) through `SessionEngine::finish_transaction_if_bound`. `commitTransaction`/`abortTransaction` without an active binding replay that outcome: repeated commit is `ok`, abort-after-commit is `TransactionCommitted` (256), commit-after-abort is `NoSuchTransaction` (251), and an older `txnNumber` on start is `TransactionTooOld` (225). Outcomes are process-local; after a crash the outcome of an in-flight commit is discoverable only from the data, which is why callers must journal their own attempts inside the transaction.
9
+ - `rustdb-commands/src/deadline.rs` holds the `maxTimeMS` deadline in a tokio task-local set by `CommandRouter::route_with_response_policy`. Checks are cooperative: admission, `deadline::wait_bounded` inside `acquire_write_lock`/`acquire_maintenance_write_gate`, `wait_until_deadline` around the maintenance read lease, `check()` after materialization in readers, `check_every` in insert/delete loops, and one final `check()` before `apply_write_batch_with_completion` in commit. Handler futures are not cancellation-safe, so never wrap a handler in `tokio::time::timeout`; add a check at the next safe point instead. `SMARTDB_TEST_OPERATION_DELAY=<command>:<ms>` (test-support builds only) injects a pre-dispatch delay for deterministic expiry tests.
10
+ - Error labels are derived from the response code in `attach_error_labels` so pre-dispatch rejections and handler errors label identically: transaction statements with 112/246/251 carry `TransientTransactionError`; `commitTransaction` with 50 or 64 carries `UnknownTransactionCommitResult`.
11
+
12
+ ## Storage VNext A0 Committed Log
13
+
14
+ - `rustdb-kernel` is a fixture-only, non-serving A0 durability slice. It is not linked into the released `rustdb` binary and provides no BSON, CRUD, MVCC, concern, deadline, migration, replication, or availability behavior.
15
+ - The immutable `kernel-manifest.sdb` stores format, group, timeline, and fixed segment identity only. It never stores a mutable commit frontier. Complete prepared/commit-marker pairs in `commit-0000000000000001.sdb` are the sole A0 commit authority.
16
+ - Each new command validates and prepares before append, receives the next contiguous nonzero LSN, and appends one `KPR1` prepared frame plus one `KCM1` marker binding the exact prepared bytes. Exact retries return the original LSN/response without appending; fingerprint conflicts, invalid commands, and segment exhaustion consume no LSN or state.
17
+ - Any append or sync error after write admission is ambiguous and permanently fences that handle, including reads and exact retries. Reopen is the only reconciliation path. Reopen syncs every complete recovered segment before exposing state or receipts.
18
+ - Recovery accepts only the declared timeline, contiguous LSNs, valid CRC frames, a matching marker position/hash, valid commands, and one durable record per receipt identity. It may truncate only an incomplete final frame of the expected kind or a complete valid prepared record without a complete marker. Complete corruption, wrong-kind tails, invalid prepared records, holes, duplicates, and marker mismatches fail without changing the segment.
19
+ - The descriptor-safe root/framing primitives are shared by `rustdb-kernel` and `rustdb-replication`. Their common parent-slot/in-root lock namespace is intentional: kernel and Raft roots must be exclusive and are never interchangeable.
20
+
3
21
  ## Authoritative File Recovery
4
22
 
5
23
  - `data.rdb` is the sole runtime source for rebuilding KeyDir. The v1 `keydir.hint` format cannot prove completeness or data-generation identity, so existing regular hint files are tolerated for compatibility and diagnostics but their contents are never loaded as runtime state. New shutdowns, compactions, and v0-to-v1 migrations do not publish hints.
package/readme.md CHANGED
@@ -354,7 +354,7 @@ Legacy v0 JSON collections are converted into hidden sibling staging directories
354
354
 
355
355
  Persisted users also carry a random principal identity and a monotonic generation. SmartDB reloads and resolves that identity for every authenticated command, so password or role changes take effect immediately and stale sockets are rejected. Deleting and recreating the same username creates a different principal; an old connection cannot inherit the replacement user's authority. Cross-process user updates are serialized through the persisted users-file lock.
356
356
 
357
- Single-node transactions are supported through official MongoDB driver sessions. `find`, `count`, `distinct`, `insert`, `update`, `findAndModify`, and `delete` use the transaction snapshot and buffered write set; `commitTransaction` applies that write set with conflict checks, and `abortTransaction` discards it. After applicable authentication, authorization, and allocation-policy checks, collection, database, and index DDL, user-management mutations, and aggregate pipelines ending in `$out` or `$merge` reject transaction envelopes with `OperationNotSupportedInTransaction` (code 263) before session or transaction creation, maintenance gates, or mutation. Live logical sessions remain resumable across socket disconnects. Bounded background cleanup aborts expired transactions, removes expired sessions, and releases publication leases; explicit `endSessions` and `killSessions` do the same for their active transactions.
357
+ Single-node transactions are supported through official MongoDB driver sessions. `find`, `count`, `distinct`, `insert`, `update`, `findAndModify`, and `delete` use the transaction snapshot and buffered write set; `commitTransaction` applies that write set with conflict checks, and `abortTransaction` discards it. The snapshot is database-wide: the first namespace a transaction materializes pins a publication sequence on the namespace clock, and any later namespace that was published after that sequence fails closed with `SnapshotUnavailable` (code 246) and the `TransientTransactionError` label so the driver restarts the transaction on fresh state; write-write conflicts surface at commit as `WriteConflict` (code 112) with the same label. Each session remembers the outcome of its last transaction number: a repeated `commitTransaction` replays `ok`, `abortTransaction` after a commit reports `TransactionCommitted` (code 256), `commitTransaction` after an abort reports `NoSuchTransaction` (code 251), and starting a transaction with an older number reports `TransactionTooOld` (code 225). After applicable authentication, authorization, and allocation-policy checks, collection, database, and index DDL, user-management mutations, and aggregate pipelines ending in `$out` or `$merge` reject transaction envelopes with `OperationNotSupportedInTransaction` (code 263) before session or transaction creation, maintenance gates, or mutation. Live logical sessions remain resumable across socket disconnects. Bounded background cleanup aborts expired transactions, removes expired sessions, and releases publication leases; explicit `endSessions` and `killSessions` do the same for their active transactions.
358
358
 
359
359
  ### Durable Database Publication Holds
360
360
 
@@ -497,6 +497,7 @@ await client.db('admin').command({ usersInfo: 'reader' });
497
497
  | `port` | `number` | Actual bound port while running; otherwise the configured port (TCP mode) |
498
498
  | `host` | `string` | Configured host (TCP mode) |
499
499
  | `socketPath` | `string \| undefined` | Socket path (socket mode) |
500
+ | `processId` | `number \| undefined` | Operating-system pid of the spawned Rust engine while it runs as a child process (for crash and kill tests) |
500
501
  | `getMetrics()` | `Promise<ISmartDbMetrics>` | Server metrics (db/collection counts, sessions, transactions, auth, uptime) |
501
502
  | `getOpLog(params?)` | `Promise<IOpLogResult>` | Query oplog entries with optional filters |
502
503
  | `getOpLogStats()` | `Promise<IOpLogStats>` | Aggregate oplog statistics |
@@ -999,22 +1000,56 @@ than a claim about exact allocator heap usage.
999
1000
 
1000
1001
  ### Read and Write Concerns and Wire Deadlines
1001
1002
 
1002
- SmartDB 5.x does not implement MongoDB `readConcern` or `writeConcern`
1003
- semantics. A command containing either top-level field is rejected with
1004
- `InvalidOptions` (code 72) before authentication, session creation, transaction
1005
- preparation, publication-gate acquisition, or database mutation. Callers must not
1006
- interpret a successful SmartDB 5.x response as a majority or replica-backed
1007
- acknowledgement. The exact `writeConcern: { w: 0 }` envelope used by the official
1008
- driver for `endSessions`, and the equivalent envelope on `killSessions`, are
1009
- accepted only to preserve best-effort session cleanup; neither is a database-write
1010
- acknowledgement.
1011
-
1012
- SmartDB 5.x also does not enforce MongoDB wire `maxTimeMS` or `maxCommitTimeMS`
1013
- deadlines. A command containing either top-level field is rejected at the same
1014
- early boundary with `InvalidOptions` (code 72). The official driver transmits a
1015
- transaction's `maxCommitTimeMS` as `maxTimeMS` on `commitTransaction`, which is
1016
- therefore rejected rather than silently ignored. These wire fields are distinct
1017
- from the fail-stop `timeoutMs` option on SmartDB's TypeScript management methods.
1003
+ SmartDB serves every database from exactly one voting node and states its
1004
+ concern profile in those terms instead of ignoring or blanket-rejecting the
1005
+ fields:
1006
+
1007
+ - **Write concern.** Every acknowledged write is fsynced to the write-ahead log
1008
+ and the data file before the response is sent, so `w: 1`, `w: "majority"`
1009
+ (the majority of one voter is that voter), and `j: true` are all satisfied by
1010
+ the same durable commit. `w: 0` is acknowledged the same way whenever a
1011
+ response is expected; the exact `writeConcern: { w: 0 }` `endSessions` and
1012
+ `killSessions` envelopes still run without a transport response. `wtimeout`
1013
+ bounds a replication wait that does not exist on one node and has no
1014
+ additional effect. `w: N` for `N > 1` fails with `UnsatisfiableWriteConcern`
1015
+ (code 100), tag-set modes fail with `UnknownReplWriteConcern` (code 79), and
1016
+ `j: true` fails with `BadValue` (code 2) on the in-memory storage backend,
1017
+ which does not journal. Write concern is accepted only on commands that
1018
+ write (including DDL, user management, `commitTransaction`, and
1019
+ `abortTransaction`) and is rejected with `InvalidOptions` (code 72) inside a
1020
+ multi-statement transaction, where it belongs on the commit.
1021
+ - **Read concern.** `local`, `available`, `majority`, and `linearizable` all
1022
+ observe the durably committed state, because nothing is acknowledged before
1023
+ it is durable and reads never observe uncommitted transaction buffers.
1024
+ `snapshot` is accepted only on the first statement of a multi-document
1025
+ transaction and is served by the database-wide transaction snapshot
1026
+ described below. Read concern is accepted on `find`, `count`, `distinct`,
1027
+ and `aggregate` only, and `afterClusterTime`, `atClusterTime`, and
1028
+ `afterOpTime` are rejected with `InvalidOptions` because a single node issues
1029
+ no cluster time.
1030
+ - **Deadlines.** `maxTimeMS` attaches a cooperative deadline to the command.
1031
+ It is checked at admission, during namespace-lock and maintenance-gate
1032
+ waits, before `find`, `count`, and `distinct` materialize documents, between
1033
+ documents in `insert` and `delete` batches, and immediately before a
1034
+ transaction commit is handed to storage; expiry fails with `MaxTimeMSExpired`
1035
+ (code 50). Database-permit waits keep their fixed internal timeout, and a
1036
+ single storage step or an in-progress `update` batch runs to completion
1037
+ before the next check, so a write that expires after its first documents
1038
+ were published reports the expiry while those documents remain durable,
1039
+ exactly like an interrupted MongoDB batch. The official driver transmits a
1040
+ transaction's `maxCommitTimeMS` as `maxTimeMS` on `commitTransaction`; a
1041
+ commit that expires before its batch is handed to storage publishes nothing,
1042
+ records the transaction as aborted, and carries the
1043
+ `UnknownTransactionCommitResult` label; the driver's commit retry then sees
1044
+ `NoSuchTransaction` with `TransientTransactionError` and restarts the whole
1045
+ transaction. `getMore` rejects `maxTimeMS` with `BadValue` because SmartDB
1046
+ has no awaitData cursors, and a literal `maxCommitTimeMS` field is rejected
1047
+ with `InvalidOptions`.
1048
+
1049
+ Callers may therefore rely on a successful SmartDB response as a durable,
1050
+ single-node acknowledgement. They must not read it as a replica-backed
1051
+ acknowledgement: majority semantics across several voters arrive with
1052
+ replication.
1018
1053
 
1019
1054
  ### Unsupported Administrative Commands
1020
1055
 
@@ -1034,7 +1069,7 @@ available and reports SmartDB's fixed disabled state.
1034
1069
 
1035
1070
  ## Rust Crate Architecture 🦀
1036
1071
 
1037
- The Rust engine is organized as a Cargo workspace with 11 focused crates:
1072
+ The Rust engine is organized as a Cargo workspace with 12 focused crates:
1038
1073
 
1039
1074
  | Crate | Purpose |
1040
1075
  |---|---|
@@ -1043,6 +1078,7 @@ The Rust engine is organized as a Cargo workspace with 11 focused crates:
1043
1078
  | `rustdb-wire` | Wire protocol parser/encoder (OP_MSG, OP_QUERY, OP_REPLY) |
1044
1079
  | `rustdb-query` | Query matcher, update engine, aggregation, sort, projection |
1045
1080
  | `rustdb-state` | Internal deterministic Storage VNext commands, stable retry identities, and durable receipts |
1081
+ | `rustdb-kernel` | Internal non-serving Storage VNext committed-log kernel with stable timelines, contiguous LSNs, strict recovery, and exact receipt replay |
1046
1082
  | `rustdb-replication` | Internal non-serving OpenRaft 0.9.25 one-voter fresh-root foundation; not linked into the released `rustdb` server |
1047
1083
  | `rustdb-storage` | Storage backends (memory, file), OpLog with point-in-time replay |
1048
1084
  | `rustdb-index` | B-tree/hash indexes, query planner (IXSCAN/COLLSCAN) |
@@ -1050,7 +1086,7 @@ The Rust engine is organized as a Cargo workspace with 11 focused crates:
1050
1086
  | `rustdb-auth` | SCRAM-SHA-256 credential handling, user metadata persistence, RBAC checks |
1051
1087
  | `rustdb-commands` | 40+ command handlers wiring everything together |
1052
1088
 
1053
- Storage VNext currently opens a fresh root only on Linux, using descriptor-relative access, checks for a supported local filesystem, and dual parent-slot/in-root leases. Stable retry identities resolve to durable receipts, including capacity-race rejection, with crash/reopen and post-quiescence snapshot/log proof. This internal foundation has no wire concerns, migration, serving listener, multi-voter transport, or HA claim. macOS and other non-Linux VNext root opens fail closed for now; existing released legacy binaries remain cross-compiled for the targets below.
1089
+ Storage VNext currently opens a fresh root only on Linux, using descriptor-relative access, checks for a supported local filesystem, and dual parent-slot/in-root leases. Its fixture-only A0 kernel appends checksummed prepared/commit-marker pairs to one bounded SmartDB-owned segment, assigns one stable timeline and contiguous LSNs, returns exact retry receipts without another append, and rebuilds materialized state before exposing a reopened root. Ambiguous append results fence the live handle; recovery syncs complete pairs before exposing them, repairs only a valid incomplete final pair, and rejects corruption or foreign roots unchanged. The separate one-voter OpenRaft foundation retains its capacity-race, crash/reopen, and snapshot/log proof, but is not yet integrated with the A0 commit kernel. These internal foundations have no BSON document substrate, MVCC, checkpoints, segment rollover, wire concerns, migration, serving listener, multi-voter transport, or HA claim. macOS and other non-Linux VNext root opens fail closed for now; existing released legacy binaries remain cross-compiled for the targets below.
1054
1090
 
1055
1091
  Cross-compiled for `linux_amd64`, `linux_arm64`, `macos_amd64`, and `macos_arm64` via [@git.zone/tsrust](https://www.npmjs.com/package/@git.zone/tsrust).
1056
1092
 
package/readme.plan.md CHANGED
@@ -3,7 +3,7 @@
3
3
  Status: canonical direction, approved 2026-08-17. Reread before starting engine or
4
4
  managed-service work.
5
5
 
6
- Baseline: `@push.rocks/smartdb` 5.3.1. SmartDB is a Rust database engine with a
6
+ Baseline: `@push.rocks/smartdb` 5.5.0. SmartDB is a Rust database engine with a
7
7
  TypeScript lifecycle facade, a MongoDB wire-protocol command surface, file and
8
8
  memory storage, authentication, transactions, resource fencing, and official
9
9
  MongoDB Node.js driver integration.
@@ -16,9 +16,24 @@ Current execution status:
16
16
  legacy hint contents, streams CRC validation, and repairs only an incomplete
17
17
  final record-header prefix
18
18
  - strict latest-per-key runtime WAL recovery and post-publication point-write
19
- fencing are implemented in the current Pending 5.x maintenance slice; durable
20
- retry receipts, crash-safe ordinary DDL, debug-server security, and general
21
- crash/soak qualification remain separate required slices
19
+ fencing were released in 5.3.2; crash-safe ordinary DDL, debug-server security,
20
+ and general crash/soak qualification remain separate required 5.x slices
21
+ - 5.4.0 released the non-serving, fixture-only A0 committed-log kernel with one
22
+ static timeline, contiguous LSNs, exact retry receipts, bounded
23
+ change-proportional appends, strict pair recovery, and ambiguity fencing; the
24
+ follow-up A1/MVCC/rollover/checkpoint work is parked unreleased on the
25
+ `storage-vnext-a1-wip` branch
26
+ - A0 has no BSON document substrate, MVCC, checkpoint, segment rollover, OpenRaft
27
+ integration, wire serving, migration, or availability claim; Phase 3B and the
28
+ earlier Phase 0/1 gates have not passed
29
+ - 5.5.0 serves the truthful single-node concern profile on the legacy engine:
30
+ fsync-backed `w: 1`/`majority`/`j` acknowledgement, committed-state reads for
31
+ `local`/`available`/`majority`/`linearizable`, database-wide transaction
32
+ snapshots pinned by the namespace publication clock with fail-closed
33
+ `SnapshotUnavailable` retries, cooperative `maxTimeMS` deadlines, and
34
+ per-session idempotent commit/abort outcomes; unsatisfiable `w: N > 1`, tag
35
+ sets, cluster-time read concerns, and journaled writes on the memory backend
36
+ remain rejected
22
37
 
23
38
  ## Product objective
24
39
 
@@ -131,7 +146,10 @@ and reproducible baseline measurements.
131
146
  ## Phase 1: truthful single-node behavior
132
147
 
133
148
  - stop over-advertising MongoDB version, wire behavior, and batch limits
134
- - reject ignored concerns, options, commands, and successful administrative stubs
149
+ - serve the single-node concern profile truthfully and reject every concern,
150
+ option, command, or administrative stub whose guarantee one node cannot
151
+ provide (completed 2026-09-02: concerns, deadlines, database-wide transaction
152
+ snapshots, idempotent transaction outcomes)
135
153
  - completed slice (2026-08-25): validate OP_MSG required flags, CRC-32C
136
154
  checksums, bounded framing, and exact no-response admission and execution
137
155
  - preserve complete BSON type and value identity for document and index keys
@@ -214,10 +232,12 @@ Gates:
214
232
  - add configurable service classes and fair scheduling
215
233
  - add only features selected by the certified compatibility profile
216
234
 
217
- Semantic work may proceed after Phase 2. Retryable outcomes, transaction publication,
218
- and durable index publication depend on Storage VNext. Unsupported local concerns
219
- remain rejected until Storage VNext implements them; majority concerns remain
220
- rejected until replication implements them.
235
+ Semantic work may proceed after Phase 2. Crash-durable retryable outcomes,
236
+ transaction publication, and durable index publication move onto Storage VNext.
237
+ The single-node concern profile is served today: every acknowledged write is
238
+ durable before the response, reads observe committed state, and transactions
239
+ observe one database-wide snapshot. Multi-voter majority acknowledgement and
240
+ cluster-time read concerns remain rejected until replication implements them.
221
241
 
222
242
  Gate: bounded-memory scans at maximum declared scale, published p99 latency and
223
243
  capacity curves, proven tenant isolation, and zero certified-profile mismatches.
@@ -251,7 +271,8 @@ logged state.
251
271
  - replicate the Storage VNext log across three replicas in separate failure domains
252
272
  - add terms, quorum commit, snapshot installation, log catch-up, and membership
253
273
  changes
254
- - implement truthful local and majority concerns
274
+ - extend the single-node concern profile to multi-voter majority acknowledgement
275
+ and cluster-time read concerns
255
276
  - publish accurate `hello` topology and election metadata
256
277
  - add causal times, retryable outcomes, idempotent transaction commits, and
257
278
  failover-safe session behavior
@@ -3,7 +3,7 @@
3
3
  The Rust executables distributed with this package include
4
4
  third-party components. This document covers the complete third-party package
5
5
  set recorded by `rust/Cargo.lock`, including target-inactive and compile-time
6
- components. The eleven `rustdb*` workspace crates are covered by the package's
6
+ components. The twelve `rustdb*` workspace crates are covered by the package's
7
7
  own MIT license and are not repeated below.
8
8
 
9
9
  ## Component Inventory
@@ -3,6 +3,6 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@push.rocks/smartdb',
6
- version: '5.3.2',
6
+ version: '5.5.0',
7
7
  description: 'A MongoDB-compatible embedded database server with wire protocol support, backed by a high-performance Rust engine.'
8
8
  }
@@ -389,6 +389,18 @@ export function getRustDbAllocatorEnv(
389
389
  */
390
390
  export class RustDbBridge extends EventEmitter {
391
391
  private bridge: plugins.smartrust.RustBridge<TSmartDbCommands>;
392
+
393
+ /**
394
+ * Operating-system process id of the spawned Rust engine while it runs as a
395
+ * child process of this Node.js process. Undefined before spawn, after
396
+ * termination, or when the bridge is connected to an external process.
397
+ */
398
+ public get processId(): number | undefined {
399
+ const transport = (this.bridge as unknown as {
400
+ transport?: { childProcess?: { pid?: number } | null };
401
+ }).transport;
402
+ return transport?.childProcess?.pid;
403
+ }
392
404
  private terminationConfirmed = true;
393
405
  private terminationPromise: Promise<void> | undefined;
394
406
  private terminationFailure: unknown;
@@ -387,6 +387,11 @@ export class SmartdbServer {
387
387
  /**
388
388
  * Check if the server is running
389
389
  */
390
+ /** Process id of the spawned Rust engine, when it runs as a child process. */
391
+ get processId(): number | undefined {
392
+ return this.bridge.processId;
393
+ }
394
+
390
395
  get running(): boolean {
391
396
  return this.isRunning;
392
397
  }