@lossless.org/nosqldb 10.10.0 → 11.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/.smartconfig.json +21 -19
- package/dist_rust/rustdb_linux_amd64 +0 -0
- package/dist_rust/rustdb_linux_amd64.tsrust-build.json +5 -5
- package/dist_rust/rustdb_linux_amd64_musl +0 -0
- package/dist_rust/rustdb_linux_amd64_musl.tsrust-build.json +5 -5
- package/dist_rust/rustdb_linux_arm64 +0 -0
- package/dist_rust/rustdb_linux_arm64.tsrust-build.json +5 -5
- package/dist_rust/rustdb_linux_arm64_musl +0 -0
- package/dist_rust/rustdb_linux_arm64_musl.tsrust-build.json +5 -5
- package/dist_rust/rustdb_macos_amd64 +0 -0
- package/dist_rust/rustdb_macos_amd64.tsrust-build.json +5 -5
- package/dist_rust/rustdb_macos_arm64 +0 -0
- package/dist_rust/rustdb_macos_arm64.tsrust-build.json +5 -5
- package/dist_ts/00_commitinfo_data.js +2 -2
- package/dist_ts/ts_local/classes.localnosqldb.d.ts +15 -4
- package/dist_ts/ts_local/classes.localnosqldb.js +31 -11
- package/dist_ts/ts_nosqldb/index.d.ts +3 -3
- package/dist_ts/ts_nosqldb/index.js +5 -4
- package/dist_ts/ts_nosqldb/rust-db-bridge.d.ts +125 -2
- package/dist_ts/ts_nosqldb/rust-db-bridge.js +261 -46
- package/dist_ts/ts_nosqldb/server/classes.nosqldbserver.d.ts +69 -3
- package/dist_ts/ts_nosqldb/server/classes.nosqldbserver.js +91 -12
- package/dist_ts/ts_nosqldb/server/index.d.ts +2 -2
- package/dist_ts/ts_nosqldb/server/index.js +2 -2
- package/dist_ts/ts_nosqldb/stopped-storage.d.ts +9 -1
- package/dist_ts/ts_nosqldb/stopped-storage.js +1 -1
- package/dist_ts/ts_nosqldb/storage-rehearsal.js +1 -1
- package/dist_ts/ts_nosqldb/storage-root-alignment.js +7 -3
- package/dist_ts/ts_nosqldb/storage-root-relocation.js +2 -2
- package/package.json +20 -16
- package/readme.md +469 -666
- package/third-party-notices.md +40 -8
- package/third_party_licenses/inventory.json +6 -6
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/ts_local/classes.localnosqldb.ts +35 -12
- package/ts/ts_nosqldb/index.ts +7 -2
- package/ts/ts_nosqldb/rust-db-bridge.ts +291 -48
- package/ts/ts_nosqldb/server/classes.nosqldbserver.ts +151 -15
- package/ts/ts_nosqldb/server/index.ts +2 -2
- package/ts/ts_nosqldb/stopped-storage.ts +9 -1
- package/ts/ts_nosqldb/storage-rehearsal.ts +1 -1
- package/ts/ts_nosqldb/storage-root-alignment.ts +6 -2
- package/ts/ts_nosqldb/storage-root-relocation.ts +1 -1
|
@@ -6,6 +6,7 @@ import { normalizeNoSqlDbFileStorageOptions } from './file-storage-options.js';
|
|
|
6
6
|
import type { TNoSqlDbFileStorageStartup } from './file-storage-startup.js';
|
|
7
7
|
import * as path from 'node:path';
|
|
8
8
|
import * as url from 'node:url';
|
|
9
|
+
import type { ChildProcess } from 'node:child_process';
|
|
9
10
|
import { EventEmitter } from 'node:events';
|
|
10
11
|
import {
|
|
11
12
|
normalizeNoSqlDbStorageRehearsalInput,
|
|
@@ -163,6 +164,92 @@ export interface IDocumentsResult {
|
|
|
163
164
|
total: number;
|
|
164
165
|
}
|
|
165
166
|
|
|
167
|
+
/**
|
|
168
|
+
* Bounds of the engine standard-error tail that start failures, unexpected
|
|
169
|
+
* engine exits and failed management calls carry: the engine's last 64 lines.
|
|
170
|
+
* A longer line keeps its first 1,024 characters, followed by
|
|
171
|
+
* ` [<n> more characters]`.
|
|
172
|
+
*/
|
|
173
|
+
export const noSqlDbEngineStderrTailLimits = Object.freeze({ lines: 64, lineCharacters: 1024 });
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Attach the engine's last standard-error lines to an error as its own field
|
|
177
|
+
* `engineStderrTail`, leaving its message and cause unchanged. Values that
|
|
178
|
+
* cannot carry a field (primitives, frozen objects) are left as they are.
|
|
179
|
+
*/
|
|
180
|
+
export function attachNoSqlDbEngineStderrTail(errorArg: unknown, tailArg: readonly string[]): void {
|
|
181
|
+
if (errorArg === null || (typeof errorArg !== 'object' && typeof errorArg !== 'function')) return;
|
|
182
|
+
if (!Object.isExtensible(errorArg) && !Object.prototype.hasOwnProperty.call(errorArg, 'engineStderrTail')) return;
|
|
183
|
+
try {
|
|
184
|
+
Object.defineProperty(errorArg, 'engineStderrTail', {
|
|
185
|
+
value: Object.freeze([...tailArg]),
|
|
186
|
+
enumerable: true,
|
|
187
|
+
configurable: true,
|
|
188
|
+
writable: false,
|
|
189
|
+
});
|
|
190
|
+
} catch {
|
|
191
|
+
// A non-configurable field of the same name stays as the caller made it.
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* The engine process exited with a non-zero status or by a signal while its
|
|
197
|
+
* server was running and no stop was in progress. `engineStderrTail` holds
|
|
198
|
+
* its last standard-error lines.
|
|
199
|
+
*/
|
|
200
|
+
export class NoSqlDbEngineExitError extends Error {
|
|
201
|
+
public readonly engineStderrTail: readonly string[];
|
|
202
|
+
|
|
203
|
+
constructor(
|
|
204
|
+
public readonly exitCode: number | null,
|
|
205
|
+
public readonly signal: string | null,
|
|
206
|
+
engineStderrTailArg: readonly string[],
|
|
207
|
+
) {
|
|
208
|
+
super(`nosqldb Rust process exited unexpectedly (code=${exitCode}, signal=${signal})`);
|
|
209
|
+
this.name = 'NoSqlDbEngineExitError';
|
|
210
|
+
this.engineStderrTail = Object.freeze([...engineStderrTailArg]);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* A start that the engine refused because the storage root, or a feature it
|
|
216
|
+
* carries, comes from a later NoSQLDB release than this one: the engine's
|
|
217
|
+
* management reply carried the code `UNKNOWN_ROOT`. The engine wrote nothing
|
|
218
|
+
* into the root (storage-root-names-and-start.md, "Roots of later releases";
|
|
219
|
+
* requirements A300 and A319). `code` holds the code, and `cause` the
|
|
220
|
+
* management error that carried it.
|
|
221
|
+
*/
|
|
222
|
+
export class NoSqlDbUnknownRootError extends Error {
|
|
223
|
+
public readonly code = 'UNKNOWN_ROOT' as const;
|
|
224
|
+
public readonly responseErrorCode = 'UNKNOWN_ROOT' as const;
|
|
225
|
+
|
|
226
|
+
constructor(options?: { cause?: unknown }) {
|
|
227
|
+
super('NoSQLDB refuses this storage root: the root, or a feature it carries, comes from a later NoSQLDB release than this one', options);
|
|
228
|
+
this.name = 'NoSqlDbUnknownRootError';
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/** The bounded record of one engine process's last standard-error lines. */
|
|
233
|
+
class EngineStderrTail {
|
|
234
|
+
private lines: string[] = [];
|
|
235
|
+
|
|
236
|
+
public add(lineArg: string): void {
|
|
237
|
+
const limit = noSqlDbEngineStderrTailLimits.lineCharacters;
|
|
238
|
+
this.lines.push(lineArg.length > limit
|
|
239
|
+
? `${lineArg.slice(0, limit)} [${lineArg.length - limit} more characters]`
|
|
240
|
+
: lineArg);
|
|
241
|
+
if (this.lines.length > noSqlDbEngineStderrTailLimits.lines) this.lines.shift();
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
public clear(): void {
|
|
245
|
+
this.lines = [];
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
public snapshot(): string[] {
|
|
249
|
+
return [...this.lines];
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
166
253
|
export class NoSqlDbManagementOperationTerminationError extends AggregateError {
|
|
167
254
|
constructor(operationErrorArg: unknown, terminationErrorArg: unknown) {
|
|
168
255
|
super(
|
|
@@ -363,6 +450,7 @@ interface INoSqlDbRustConfig {
|
|
|
363
450
|
storagePath?: string;
|
|
364
451
|
query?: INoSqlDbQueryOptions;
|
|
365
452
|
inbound?: INoSqlDbInboundOptions;
|
|
453
|
+
storageBudgetBytes?: number;
|
|
366
454
|
auth?: {
|
|
367
455
|
enabled?: boolean;
|
|
368
456
|
users?: Array<{
|
|
@@ -397,8 +485,8 @@ function getPackageRoot(): string {
|
|
|
397
485
|
}
|
|
398
486
|
|
|
399
487
|
/**
|
|
400
|
-
* Map Node.js process.platform/process.arch to
|
|
401
|
-
*
|
|
488
|
+
* Map Node.js process.platform/process.arch to rsrepo's friendly name suffix.
|
|
489
|
+
* rsrepo names cross-compiled binaries as: rustdb_linux_amd64, rustdb_linux_arm64, etc.
|
|
402
490
|
*/
|
|
403
491
|
function getTsrustPlatformSuffix(): string | null {
|
|
404
492
|
const archMap: Record<string, string> = { x64: 'amd64', arm64: 'arm64' };
|
|
@@ -414,14 +502,14 @@ function getTsrustPlatformSuffix(): string | null {
|
|
|
414
502
|
|
|
415
503
|
/**
|
|
416
504
|
* Build local search paths for the Rust binary, including dist_rust/ candidates
|
|
417
|
-
* (built by
|
|
505
|
+
* (built by rsrepo) and local development build paths.
|
|
418
506
|
*/
|
|
419
507
|
function buildLocalPaths(): string[] {
|
|
420
508
|
const packageRoot = getPackageRoot();
|
|
421
509
|
const suffix = getTsrustPlatformSuffix();
|
|
422
510
|
const paths: string[] = [];
|
|
423
511
|
|
|
424
|
-
// dist_rust/ candidates (
|
|
512
|
+
// dist_rust/ candidates (rsrepo cross-compiled output)
|
|
425
513
|
if (suffix) {
|
|
426
514
|
paths.push(path.join(packageRoot, 'dist_rust', `rustdb_${suffix}`));
|
|
427
515
|
}
|
|
@@ -433,18 +521,40 @@ function buildLocalPaths(): string[] {
|
|
|
433
521
|
|
|
434
522
|
return paths;
|
|
435
523
|
}
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
524
|
+
|
|
525
|
+
const binaryHelp = 'Set NOSQLDB_RUST_BINARY env var, install the platform package, or build locally with `rsrepo`.';
|
|
526
|
+
|
|
527
|
+
/**
|
|
528
|
+
* What a spawn() that returned false after `waitedMs` means, stating only what
|
|
529
|
+
* is known. Smartrust refuses a `binaryPath` that does not pass its validation
|
|
530
|
+
* before spawn() can return false, so with one the engine there passed
|
|
531
|
+
* Smartrust's validation and did not report ready within the bridge's limit,
|
|
532
|
+
* and its path is given as the caller gave it.
|
|
533
|
+
* Without one, whatever NOSQLDB_RUST_BINARY holds (one of the places that
|
|
534
|
+
* Smartrust's search looks), either the search found no engine or the engine
|
|
535
|
+
* it found did not report ready, and nothing tells which.
|
|
536
|
+
*/
|
|
537
|
+
export function describeRefusedSpawn(binaryPath: string | undefined, waitedMs: number): string {
|
|
538
|
+
if (binaryPath !== undefined) {
|
|
539
|
+
return `the nosqldb engine at ${binaryPath} did not report ready within the bridge's limit: it waited ${waitedMs} ms`;
|
|
540
|
+
}
|
|
541
|
+
return `the nosqldb engine did not start: spawn() returned false after ${waitedMs} ms, which means either that no nosqldb Rust binary `
|
|
542
|
+
+ `was found (${binaryHelp}) or that the engine found did not report ready within the bridge's limit`;
|
|
443
543
|
}
|
|
444
544
|
|
|
445
545
|
export interface IRustDbBridgeOptions {
|
|
446
546
|
/** Exact engine executable, validated by Smartrust without discovery fallback or permission changes. */
|
|
447
547
|
binaryPath?: string;
|
|
548
|
+
/**
|
|
549
|
+
* Receives every line the engine writes to its standard error, in order and
|
|
550
|
+
* without its line break. Without it, no engine line reaches the host
|
|
551
|
+
* process's standard error. Either way the bridge keeps the last lines for
|
|
552
|
+
* errors (`noSqlDbEngineStderrTailLimits`). Each call runs in its own
|
|
553
|
+
* microtask after the bridge has recorded the line, so an exception it
|
|
554
|
+
* throws cannot disturb the bridge; it is not caught and reaches the process
|
|
555
|
+
* as an uncaught exception.
|
|
556
|
+
*/
|
|
557
|
+
engineLog?: (line: string) => void;
|
|
448
558
|
}
|
|
449
559
|
|
|
450
560
|
/**
|
|
@@ -460,17 +570,100 @@ export class RustDbBridge extends EventEmitter {
|
|
|
460
570
|
* termination, or when the bridge is connected to an external process.
|
|
461
571
|
*/
|
|
462
572
|
public get processId(): number | undefined {
|
|
573
|
+
return this.childProcess?.pid;
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
/** The engine child process of the latest spawn, while the bridge holds it. */
|
|
577
|
+
private get childProcess(): ChildProcess | undefined {
|
|
463
578
|
const transport = (this.bridge as unknown as {
|
|
464
|
-
transport?: { childProcess?:
|
|
579
|
+
transport?: { childProcess?: ChildProcess | null };
|
|
465
580
|
}).transport;
|
|
466
|
-
return transport?.childProcess
|
|
581
|
+
return transport?.childProcess ?? undefined;
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
/** How the engine process of the latest spawn exited, once it has. */
|
|
585
|
+
private engineExit: { code: number | null; signal: string | null } | null = null;
|
|
586
|
+
private engineExitPromise: Promise<void> | undefined;
|
|
587
|
+
|
|
588
|
+
/** Records the exit of the engine process that a spawn has just started. */
|
|
589
|
+
private watchEngineExit(): void {
|
|
590
|
+
const child = this.childProcess;
|
|
591
|
+
if (!child) return;
|
|
592
|
+
this.engineExitPromise = new Promise((resolveArg) => {
|
|
593
|
+
const record = (code: number | null, signal: string | null) => {
|
|
594
|
+
this.engineExit = { code, signal };
|
|
595
|
+
resolveArg();
|
|
596
|
+
};
|
|
597
|
+
if (child.exitCode !== null || child.signalCode !== null) record(child.exitCode, child.signalCode);
|
|
598
|
+
else child.once('exit', record);
|
|
599
|
+
});
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
/**
|
|
603
|
+
* The exit code and signal of the engine process of the latest spawn, as its
|
|
604
|
+
* process exit reported them, each null where it reported none; or null
|
|
605
|
+
* while that process has not exited, or no spawn started one.
|
|
606
|
+
*/
|
|
607
|
+
public get lastEngineExit(): { code: number | null; signal: string | null } | null {
|
|
608
|
+
return this.engineExit;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
/** Whether the engine process of the latest spawn exists and has not exited. */
|
|
612
|
+
public get engineProcessAlive(): boolean {
|
|
613
|
+
return this.engineExitPromise !== undefined && this.engineExit === null;
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
/** Waits up to `timeoutMs` for the engine process to exit by itself; resolves whether it has. */
|
|
617
|
+
public async waitForEngineExit(timeoutMs: number): Promise<boolean> {
|
|
618
|
+
if (!this.engineExitPromise) return false;
|
|
619
|
+
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
620
|
+
await Promise.race([
|
|
621
|
+
this.engineExitPromise,
|
|
622
|
+
new Promise<void>((resolveArg) => { timer = setTimeout(resolveArg, timeoutMs); }),
|
|
623
|
+
]);
|
|
624
|
+
if (timer) clearTimeout(timer);
|
|
625
|
+
return this.engineExit !== null;
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
/**
|
|
629
|
+
* Closes the engine's management input, the end of the management protocol
|
|
630
|
+
* after an acknowledged `stop`, on which the engine exits by itself. This is
|
|
631
|
+
* neither a signal nor a kill. Returns whether there was an input to close.
|
|
632
|
+
*/
|
|
633
|
+
public endManagementInput(): boolean {
|
|
634
|
+
const input = this.childProcess?.stdin;
|
|
635
|
+
if (!input || input.writableEnded) return false;
|
|
636
|
+
input.end();
|
|
637
|
+
return true;
|
|
467
638
|
}
|
|
468
639
|
private terminationConfirmed = true;
|
|
469
640
|
private terminationPromise: Promise<void> | undefined;
|
|
470
641
|
private terminationFailure: unknown;
|
|
642
|
+
private readonly stderrTail = new EngineStderrTail();
|
|
643
|
+
/** The `binaryPath` this bridge was given, if any, as given: the bridge's only configured path. */
|
|
644
|
+
private readonly binaryPath: string | undefined;
|
|
645
|
+
private spawnRefusal: string | undefined;
|
|
646
|
+
|
|
647
|
+
/**
|
|
648
|
+
* Why Smartrust's spawn() returned false, as far as the bridge knows. Each
|
|
649
|
+
* spawn() clears it as it starts, and sets it when Smartrust's spawn()
|
|
650
|
+
* returns false before this spawn()'s own `timeoutMs` or `signal` cuts it
|
|
651
|
+
* short, before the engine is ended. That spawn() then returns false, or
|
|
652
|
+
* throws if ending the engine fails. After any other spawn() it is
|
|
653
|
+
* undefined, including one cut short by its own `timeoutMs` or `signal`.
|
|
654
|
+
* Smartrust's spawn() returns false both when it finds no engine and when
|
|
655
|
+
* the engine it starts does not report ready within its limit; a
|
|
656
|
+
* `binaryPath` tells the two apart, as Smartrust refuses one that does not
|
|
657
|
+
* pass its validation with RustBinaryLocatorError before spawn() can return
|
|
658
|
+
* false.
|
|
659
|
+
*/
|
|
660
|
+
public get spawnFailureMessage(): string | undefined {
|
|
661
|
+
return this.spawnRefusal;
|
|
662
|
+
}
|
|
471
663
|
|
|
472
664
|
constructor(options: IRustDbBridgeOptions = {}) {
|
|
473
665
|
super();
|
|
666
|
+
this.binaryPath = options.binaryPath;
|
|
474
667
|
|
|
475
668
|
this.bridge = new plugins.smartrust.RustBridge<TNoSqlDbCommands>({
|
|
476
669
|
binaryName: 'rustdb',
|
|
@@ -479,7 +672,14 @@ export class RustDbBridge extends EventEmitter {
|
|
|
479
672
|
platformPackagePrefix: '@lossless.org/nosqldb',
|
|
480
673
|
localPaths: buildLocalPaths(),
|
|
481
674
|
maxPayloadSize: 100 * 1024 * 1024, // database exports/imports can be larger than command replies
|
|
482
|
-
|
|
675
|
+
});
|
|
676
|
+
|
|
677
|
+
// Smartrust delivers the engine's standard error one line at a time.
|
|
678
|
+
const engineLog = options.engineLog;
|
|
679
|
+
this.bridge.on('stderr', (line: string) => {
|
|
680
|
+
this.stderrTail.add(line);
|
|
681
|
+
// Microtasks run in order, so the lines keep their order.
|
|
682
|
+
if (engineLog) queueMicrotask(() => engineLog(line));
|
|
483
683
|
});
|
|
484
684
|
|
|
485
685
|
// Forward events from the inner bridge
|
|
@@ -489,11 +689,43 @@ export class RustDbBridge extends EventEmitter {
|
|
|
489
689
|
});
|
|
490
690
|
}
|
|
491
691
|
|
|
692
|
+
/**
|
|
693
|
+
* The last standard-error lines of the running or most recently spawned
|
|
694
|
+
* engine process, within `noSqlDbEngineStderrTailLimits`.
|
|
695
|
+
*/
|
|
696
|
+
public get engineStderrTail(): string[] {
|
|
697
|
+
return this.stderrTail.snapshot();
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
/**
|
|
701
|
+
* Send one management command. A failure while no engine runs (it exited
|
|
702
|
+
* or never started) carries the engine's last standard-error lines.
|
|
703
|
+
*/
|
|
704
|
+
private async send<TMethod extends keyof TNoSqlDbCommands & string>(
|
|
705
|
+
methodArg: TMethod,
|
|
706
|
+
paramsArg: TNoSqlDbCommands[TMethod]['params'],
|
|
707
|
+
optionsArg?: INoSqlDbManagementOperationOptions,
|
|
708
|
+
): Promise<TNoSqlDbCommands[TMethod]['result']> {
|
|
709
|
+
try {
|
|
710
|
+
return await this.bridge.sendCommand(methodArg, paramsArg, optionsArg);
|
|
711
|
+
} catch (error) {
|
|
712
|
+
if (!this.bridge.running) attachNoSqlDbEngineStderrTail(error, this.stderrTail.snapshot());
|
|
713
|
+
throw error;
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
|
|
492
717
|
/**
|
|
493
718
|
* Spawn the Rust binary in management mode.
|
|
494
|
-
* Returns true
|
|
719
|
+
* Returns true once the engine reported ready. Returns false when
|
|
720
|
+
* Smartrust's spawn() returned false: no engine was found, or the one found
|
|
721
|
+
* did not report ready within the bridge's limit. spawnFailureMessage then
|
|
722
|
+
* says what that means, as far as the bridge knows, and still says it if
|
|
723
|
+
* ending that engine fails and this throws instead. After any other
|
|
724
|
+
* outcome, including a throw on this spawn()'s own `timeoutMs` or `signal`,
|
|
725
|
+
* spawnFailureMessage is undefined.
|
|
495
726
|
*/
|
|
496
727
|
public async spawn(optionsArg?: INoSqlDbManagementOperationOptions): Promise<boolean> {
|
|
728
|
+
this.spawnRefusal = undefined;
|
|
497
729
|
validateNoSqlDbManagementOperationOptions(optionsArg);
|
|
498
730
|
if (optionsArg?.signal?.aborted) {
|
|
499
731
|
throw new plugins.smartrust.RustBridgeRequestError(
|
|
@@ -508,6 +740,11 @@ export class RustDbBridge extends EventEmitter {
|
|
|
508
740
|
|
|
509
741
|
this.terminationConfirmed = false;
|
|
510
742
|
this.terminationFailure = undefined;
|
|
743
|
+
this.stderrTail.clear();
|
|
744
|
+
const spawnStartedAt = performance.now();
|
|
745
|
+
// The previous engine's exit belongs to it, also when this spawn fails.
|
|
746
|
+
this.engineExit = null;
|
|
747
|
+
this.engineExitPromise = undefined;
|
|
511
748
|
const spawnPromise = this.bridge.spawn();
|
|
512
749
|
let timeout: ReturnType<typeof setTimeout> | undefined;
|
|
513
750
|
let abortHandler: (() => void) | undefined;
|
|
@@ -540,7 +777,9 @@ export class RustDbBridge extends EventEmitter {
|
|
|
540
777
|
|
|
541
778
|
try {
|
|
542
779
|
const spawned = await Promise.race([spawnPromise, cancellationPromise]);
|
|
780
|
+
if (spawned) this.watchEngineExit();
|
|
543
781
|
if (!spawned) {
|
|
782
|
+
this.spawnRefusal = describeRefusedSpawn(this.binaryPath, Math.round(performance.now() - spawnStartedAt));
|
|
544
783
|
await this.terminate({ gracePeriodMs: 0 });
|
|
545
784
|
}
|
|
546
785
|
return spawned;
|
|
@@ -652,7 +891,7 @@ export class RustDbBridge extends EventEmitter {
|
|
|
652
891
|
const request = normalizeNoSqlDbStorageRehearsalInput(inputArg);
|
|
653
892
|
validateNoSqlDbManagementOperationOptions(optionsArg);
|
|
654
893
|
try {
|
|
655
|
-
return await this.
|
|
894
|
+
return await this.send('createStoppedStorageRehearsal',
|
|
656
895
|
{ request, timeoutMs: optionsArg.timeoutMs }, optionsArg) as INoSqlDbStorageRehearsalReceipt;
|
|
657
896
|
} catch (error) {
|
|
658
897
|
await this.awaitManagementOperationTermination(error);
|
|
@@ -667,7 +906,7 @@ export class RustDbBridge extends EventEmitter {
|
|
|
667
906
|
const input = normalizeLocalNoSqlDbStoppedStorageRootRelocationInput(inputArg);
|
|
668
907
|
validateNoSqlDbManagementOperationOptions(optionsArg);
|
|
669
908
|
try {
|
|
670
|
-
return await this.
|
|
909
|
+
return await this.send(
|
|
671
910
|
'relocateStoppedStorageRoot',
|
|
672
911
|
{ ...input, timeoutMs: optionsArg?.timeoutMs },
|
|
673
912
|
optionsArg,
|
|
@@ -691,7 +930,7 @@ export class RustDbBridge extends EventEmitter {
|
|
|
691
930
|
const input = normalizeStoppedStorageRootAlignmentInput(inputArg);
|
|
692
931
|
validateNoSqlDbManagementOperationOptions(optionsArg);
|
|
693
932
|
try {
|
|
694
|
-
return await this.
|
|
933
|
+
return await this.send(method, { ...input, timeoutMs: optionsArg?.timeoutMs }, optionsArg);
|
|
695
934
|
} catch (error) {
|
|
696
935
|
await this.awaitManagementOperationTermination(error);
|
|
697
936
|
throw normalizeStoppedOperationUnknownMethod(error, method);
|
|
@@ -705,7 +944,7 @@ export class RustDbBridge extends EventEmitter {
|
|
|
705
944
|
const input = normalizeStoppedStorageRootInspectionInput(inputArg);
|
|
706
945
|
validateNoSqlDbManagementOperationOptions(optionsArg);
|
|
707
946
|
try {
|
|
708
|
-
return await this.
|
|
947
|
+
return await this.send('inspectStoppedStorageRoot',
|
|
709
948
|
{ ...input, timeoutMs: optionsArg?.timeoutMs }, optionsArg) as TLocalNoSqlDbStoppedStorageRootInspection;
|
|
710
949
|
} catch (error) {
|
|
711
950
|
await this.awaitManagementOperationTermination(error);
|
|
@@ -725,7 +964,7 @@ export class RustDbBridge extends EventEmitter {
|
|
|
725
964
|
timeoutMs: optionsArg?.timeoutMs,
|
|
726
965
|
};
|
|
727
966
|
try {
|
|
728
|
-
return await this.
|
|
967
|
+
return await this.send(
|
|
729
968
|
'inspectOfflinePhysicalNamespaces',
|
|
730
969
|
params,
|
|
731
970
|
optionsArg,
|
|
@@ -743,7 +982,7 @@ export class RustDbBridge extends EventEmitter {
|
|
|
743
982
|
const input = normalizeLocalNoSqlDbOfflineInspectionInput(inputArg);
|
|
744
983
|
validateNoSqlDbManagementOperationOptions(optionsArg);
|
|
745
984
|
try {
|
|
746
|
-
return await this.
|
|
985
|
+
return await this.send(
|
|
747
986
|
'inspectOfflineStringValue',
|
|
748
987
|
{ ...input, timeoutMs: optionsArg?.timeoutMs },
|
|
749
988
|
optionsArg,
|
|
@@ -761,34 +1000,38 @@ export class RustDbBridge extends EventEmitter {
|
|
|
761
1000
|
normalizeNoSqlDbFileStorageOptions(config);
|
|
762
1001
|
validateNoSqlDbManagementOperationOptions(optionsArg);
|
|
763
1002
|
try {
|
|
764
|
-
return await this.
|
|
1003
|
+
return await this.send(
|
|
765
1004
|
'start',
|
|
766
1005
|
{ config, timeoutMs: optionsArg?.timeoutMs },
|
|
767
1006
|
optionsArg,
|
|
768
1007
|
) as { connectionUri: string };
|
|
769
1008
|
} catch (error) {
|
|
770
1009
|
await this.awaitManagementOperationTermination(error);
|
|
1010
|
+
// The unknown-root refusal is told apart by its code, never by its message.
|
|
1011
|
+
if (error instanceof Error && 'responseErrorCode' in error && error.responseErrorCode === 'UNKNOWN_ROOT') {
|
|
1012
|
+
throw new NoSqlDbUnknownRootError({ cause: error });
|
|
1013
|
+
}
|
|
771
1014
|
throw error;
|
|
772
1015
|
}
|
|
773
1016
|
}
|
|
774
1017
|
|
|
775
1018
|
public async stopDb(): Promise<void> {
|
|
776
|
-
await this.
|
|
1019
|
+
await this.send('stop', {} as Record<string, never>);
|
|
777
1020
|
}
|
|
778
1021
|
|
|
779
1022
|
public async getStatus(): Promise<{ running: boolean }> {
|
|
780
|
-
return await this.
|
|
1023
|
+
return await this.send('getStatus', {} as Record<string, never>) as { running: boolean };
|
|
781
1024
|
}
|
|
782
1025
|
|
|
783
1026
|
public async getMetrics(): Promise<INoSqlDbMetrics> {
|
|
784
|
-
return this.
|
|
1027
|
+
return this.send('getMetrics', {} as Record<string, never>) as Promise<INoSqlDbMetrics>;
|
|
785
1028
|
}
|
|
786
1029
|
|
|
787
1030
|
public async getHealth(
|
|
788
1031
|
optionsArg?: INoSqlDbManagementOperationOptions,
|
|
789
1032
|
): Promise<INoSqlDbHealth> {
|
|
790
1033
|
try {
|
|
791
|
-
return await this.
|
|
1034
|
+
return await this.send(
|
|
792
1035
|
'getHealth',
|
|
793
1036
|
{} as Record<string, never>,
|
|
794
1037
|
optionsArg,
|
|
@@ -803,7 +1046,7 @@ export class RustDbBridge extends EventEmitter {
|
|
|
803
1046
|
params: INoSqlDbDatabaseTenantInput,
|
|
804
1047
|
): Promise<INoSqlDbDatabaseTenantDescriptor> {
|
|
805
1048
|
try {
|
|
806
|
-
return await this.
|
|
1049
|
+
return await this.send('createDatabaseTenant', params) as INoSqlDbDatabaseTenantDescriptor;
|
|
807
1050
|
} catch (error) {
|
|
808
1051
|
throw normalizeNoSqlDbResourceFenceError(error);
|
|
809
1052
|
}
|
|
@@ -814,7 +1057,7 @@ export class RustDbBridge extends EventEmitter {
|
|
|
814
1057
|
optionsArg?: INoSqlDbManagementOperationOptions,
|
|
815
1058
|
): Promise<INoSqlDbEnsureDatabaseTenantResult> {
|
|
816
1059
|
try {
|
|
817
|
-
return await this.
|
|
1060
|
+
return await this.send(
|
|
818
1061
|
'ensureDatabaseTenant',
|
|
819
1062
|
params,
|
|
820
1063
|
optionsArg,
|
|
@@ -830,7 +1073,7 @@ export class RustDbBridge extends EventEmitter {
|
|
|
830
1073
|
optionsArg?: INoSqlDbManagementOperationOptions,
|
|
831
1074
|
): Promise<INoSqlDbAttestDatabaseTenantResult> {
|
|
832
1075
|
try {
|
|
833
|
-
return await this.
|
|
1076
|
+
return await this.send(
|
|
834
1077
|
'attestDatabaseTenant',
|
|
835
1078
|
params,
|
|
836
1079
|
optionsArg,
|
|
@@ -846,7 +1089,7 @@ export class RustDbBridge extends EventEmitter {
|
|
|
846
1089
|
optionsArg?: INoSqlDbManagementOperationOptions,
|
|
847
1090
|
): Promise<INoSqlDbAllocateDatabaseTenantResult> {
|
|
848
1091
|
try {
|
|
849
|
-
return await this.
|
|
1092
|
+
return await this.send(
|
|
850
1093
|
'allocateDatabaseTenant',
|
|
851
1094
|
params,
|
|
852
1095
|
optionsArg,
|
|
@@ -862,7 +1105,7 @@ export class RustDbBridge extends EventEmitter {
|
|
|
862
1105
|
optionsArg?: INoSqlDbManagementOperationOptions,
|
|
863
1106
|
): Promise<INoSqlDbDeleteDatabaseTenantResult> {
|
|
864
1107
|
try {
|
|
865
|
-
return await this.
|
|
1108
|
+
return await this.send(
|
|
866
1109
|
'deleteDatabaseTenant',
|
|
867
1110
|
params,
|
|
868
1111
|
optionsArg,
|
|
@@ -878,7 +1121,7 @@ export class RustDbBridge extends EventEmitter {
|
|
|
878
1121
|
optionsArg?: INoSqlDbManagementOperationOptions,
|
|
879
1122
|
): Promise<INoSqlDbIssueDatabaseAllocationReadAccessGrantResult> {
|
|
880
1123
|
try {
|
|
881
|
-
return await this.
|
|
1124
|
+
return await this.send(
|
|
882
1125
|
'issueDatabaseAllocationReadAccessGrant',
|
|
883
1126
|
params,
|
|
884
1127
|
optionsArg,
|
|
@@ -899,7 +1142,7 @@ export class RustDbBridge extends EventEmitter {
|
|
|
899
1142
|
optionsArg?: INoSqlDbManagementOperationOptions,
|
|
900
1143
|
): Promise<INoSqlDbRevokeDatabaseAllocationReadAccessGrantResult> {
|
|
901
1144
|
try {
|
|
902
|
-
return await this.
|
|
1145
|
+
return await this.send(
|
|
903
1146
|
'revokeDatabaseAllocationReadAccessGrant',
|
|
904
1147
|
params,
|
|
905
1148
|
optionsArg,
|
|
@@ -918,11 +1161,11 @@ export class RustDbBridge extends EventEmitter {
|
|
|
918
1161
|
public async rotateDatabaseTenantPassword(
|
|
919
1162
|
params: INoSqlDbRotateDatabaseTenantPasswordInput,
|
|
920
1163
|
): Promise<INoSqlDbDatabaseTenantDescriptor> {
|
|
921
|
-
return this.
|
|
1164
|
+
return this.send('rotateDatabaseTenantPassword', params) as Promise<INoSqlDbDatabaseTenantDescriptor>;
|
|
922
1165
|
}
|
|
923
1166
|
|
|
924
1167
|
public async listDatabaseTenants(): Promise<INoSqlDbDatabaseTenantDescriptor[]> {
|
|
925
|
-
const result = await this.
|
|
1168
|
+
const result = await this.send('listDatabaseTenants', {} as Record<string, never>) as { tenants: INoSqlDbDatabaseTenantDescriptor[] };
|
|
926
1169
|
return result.tenants;
|
|
927
1170
|
}
|
|
928
1171
|
|
|
@@ -930,7 +1173,7 @@ export class RustDbBridge extends EventEmitter {
|
|
|
930
1173
|
databaseName: string;
|
|
931
1174
|
username: string;
|
|
932
1175
|
}): Promise<INoSqlDbDatabaseTenantDescriptor> {
|
|
933
|
-
return this.
|
|
1176
|
+
return this.send('getDatabaseTenantDescriptor', params) as Promise<INoSqlDbDatabaseTenantDescriptor>;
|
|
934
1177
|
}
|
|
935
1178
|
|
|
936
1179
|
public async getDatabaseResourceFenceState(
|
|
@@ -938,7 +1181,7 @@ export class RustDbBridge extends EventEmitter {
|
|
|
938
1181
|
optionsArg?: INoSqlDbManagementOperationOptions,
|
|
939
1182
|
): Promise<INoSqlDbDatabaseResourceFenceState | null> {
|
|
940
1183
|
try {
|
|
941
|
-
return await this.
|
|
1184
|
+
return await this.send(
|
|
942
1185
|
'getDatabaseResourceFenceState',
|
|
943
1186
|
params,
|
|
944
1187
|
optionsArg,
|
|
@@ -954,7 +1197,7 @@ export class RustDbBridge extends EventEmitter {
|
|
|
954
1197
|
optionsArg?: INoSqlDbManagementOperationOptions,
|
|
955
1198
|
): Promise<INoSqlDbDatabaseExport> {
|
|
956
1199
|
try {
|
|
957
|
-
return await this.
|
|
1200
|
+
return await this.send(
|
|
958
1201
|
'exportDatabase',
|
|
959
1202
|
params,
|
|
960
1203
|
optionsArg,
|
|
@@ -970,7 +1213,7 @@ export class RustDbBridge extends EventEmitter {
|
|
|
970
1213
|
optionsArg?: INoSqlDbManagementOperationOptions,
|
|
971
1214
|
): Promise<INoSqlDbDatabaseExport> {
|
|
972
1215
|
try {
|
|
973
|
-
return await this.
|
|
1216
|
+
return await this.send(
|
|
974
1217
|
'exportHeldDatabase',
|
|
975
1218
|
params,
|
|
976
1219
|
optionsArg,
|
|
@@ -986,7 +1229,7 @@ export class RustDbBridge extends EventEmitter {
|
|
|
986
1229
|
optionsArg?: INoSqlDbManagementOperationOptions,
|
|
987
1230
|
): Promise<INoSqlDbDatabaseContentDigest> {
|
|
988
1231
|
try {
|
|
989
|
-
return await this.
|
|
1232
|
+
return await this.send(
|
|
990
1233
|
'getDatabaseContentDigest',
|
|
991
1234
|
params,
|
|
992
1235
|
optionsArg,
|
|
@@ -1002,7 +1245,7 @@ export class RustDbBridge extends EventEmitter {
|
|
|
1002
1245
|
optionsArg?: INoSqlDbManagementOperationOptions,
|
|
1003
1246
|
): Promise<INoSqlDbImportDatabaseResult> {
|
|
1004
1247
|
try {
|
|
1005
|
-
return await this.
|
|
1248
|
+
return await this.send(
|
|
1006
1249
|
'importDatabase',
|
|
1007
1250
|
{ ...params, timeoutMs: optionsArg?.timeoutMs },
|
|
1008
1251
|
optionsArg,
|
|
@@ -1018,7 +1261,7 @@ export class RustDbBridge extends EventEmitter {
|
|
|
1018
1261
|
optionsArg?: INoSqlDbManagementOperationOptions,
|
|
1019
1262
|
): Promise<TNoSqlDbCommitDatabasePublicationResult> {
|
|
1020
1263
|
try {
|
|
1021
|
-
return await this.
|
|
1264
|
+
return await this.send(
|
|
1022
1265
|
'commitDatabasePublication',
|
|
1023
1266
|
params,
|
|
1024
1267
|
optionsArg,
|
|
@@ -1035,23 +1278,23 @@ export class RustDbBridge extends EventEmitter {
|
|
|
1035
1278
|
db?: string;
|
|
1036
1279
|
collection?: string;
|
|
1037
1280
|
} = {}): Promise<IOpLogResult> {
|
|
1038
|
-
return this.
|
|
1281
|
+
return this.send('getOpLog', params) as Promise<IOpLogResult>;
|
|
1039
1282
|
}
|
|
1040
1283
|
|
|
1041
1284
|
public async getCommittedWrites(params: INoSqlDbGetCommittedWritesInput): Promise<INoSqlDbCommittedWritesPage> {
|
|
1042
|
-
return this.
|
|
1285
|
+
return this.send('getCommittedWrites', params);
|
|
1043
1286
|
}
|
|
1044
1287
|
|
|
1045
1288
|
public async getOpLogStats(): Promise<IOpLogStats> {
|
|
1046
|
-
return this.
|
|
1289
|
+
return this.send('getOpLogStats', {} as Record<string, never>) as Promise<IOpLogStats>;
|
|
1047
1290
|
}
|
|
1048
1291
|
|
|
1049
1292
|
public async revertToSeq(seq: number, dryRun = false): Promise<IRevertResult> {
|
|
1050
|
-
return this.
|
|
1293
|
+
return this.send('revertToSeq', { seq, dryRun }) as Promise<IRevertResult>;
|
|
1051
1294
|
}
|
|
1052
1295
|
|
|
1053
1296
|
public async getCollections(db?: string): Promise<ICollectionInfo[]> {
|
|
1054
|
-
const result = await this.
|
|
1297
|
+
const result = await this.send('getCollections', db ? { db } : {}) as { collections: ICollectionInfo[] };
|
|
1055
1298
|
return result.collections;
|
|
1056
1299
|
}
|
|
1057
1300
|
|
|
@@ -1061,6 +1304,6 @@ export class RustDbBridge extends EventEmitter {
|
|
|
1061
1304
|
limit = 50,
|
|
1062
1305
|
skip = 0,
|
|
1063
1306
|
): Promise<IDocumentsResult> {
|
|
1064
|
-
return this.
|
|
1307
|
+
return this.send('getDocuments', { db, collection, limit, skip }) as Promise<IDocumentsResult>;
|
|
1065
1308
|
}
|
|
1066
1309
|
}
|