@lossless.org/nosqldb 10.0.0 → 10.2.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.
Files changed (42) hide show
  1. package/dist_rust/rustdb_linux_amd64 +0 -0
  2. package/dist_rust/rustdb_linux_amd64.tsrust-build.json +4 -4
  3. package/dist_rust/rustdb_linux_amd64_musl +0 -0
  4. package/dist_rust/rustdb_linux_amd64_musl.tsrust-build.json +4 -4
  5. package/dist_rust/rustdb_linux_arm64 +0 -0
  6. package/dist_rust/rustdb_linux_arm64.tsrust-build.json +4 -4
  7. package/dist_rust/rustdb_linux_arm64_musl +0 -0
  8. package/dist_rust/rustdb_linux_arm64_musl.tsrust-build.json +4 -4
  9. package/dist_rust/rustdb_macos_amd64 +0 -0
  10. package/dist_rust/rustdb_macos_amd64.tsrust-build.json +4 -4
  11. package/dist_rust/rustdb_macos_arm64 +0 -0
  12. package/dist_rust/rustdb_macos_arm64.tsrust-build.json +4 -4
  13. package/dist_ts/00_commitinfo_data.js +1 -1
  14. package/dist_ts/ts_local/classes.localnosqldb.d.ts +15 -5
  15. package/dist_ts/ts_local/classes.localnosqldb.js +119 -11
  16. package/dist_ts/ts_nosqldb/file-storage-startup.d.ts +1 -1
  17. package/dist_ts/ts_nosqldb/file-storage-startup.js +4 -2
  18. package/dist_ts/ts_nosqldb/index.d.ts +3 -0
  19. package/dist_ts/ts_nosqldb/index.js +2 -1
  20. package/dist_ts/ts_nosqldb/rust-db-bridge.d.ts +10 -0
  21. package/dist_ts/ts_nosqldb/rust-db-bridge.js +37 -5
  22. package/dist_ts/ts_nosqldb/server/classes.nosqldbserver.d.ts +1 -1
  23. package/dist_ts/ts_nosqldb/server/classes.nosqldbserver.js +2 -1
  24. package/dist_ts/ts_nosqldb/service-types.d.ts +4 -0
  25. package/dist_ts/ts_nosqldb/service-types.js +1 -1
  26. package/dist_ts/ts_nosqldb/stopped-storage.d.ts +69 -0
  27. package/dist_ts/ts_nosqldb/stopped-storage.js +44 -0
  28. package/dist_ts/ts_nosqldb/storage-root-alignment.d.ts +58 -0
  29. package/dist_ts/ts_nosqldb/storage-root-alignment.js +129 -0
  30. package/dist_ts/ts_nosqldb/storage-root-relocation.js +12 -10
  31. package/package.json +1 -1
  32. package/readme.md +162 -16
  33. package/ts/00_commitinfo_data.ts +1 -1
  34. package/ts/ts_local/classes.localnosqldb.ts +123 -15
  35. package/ts/ts_nosqldb/file-storage-startup.ts +3 -2
  36. package/ts/ts_nosqldb/index.ts +13 -0
  37. package/ts/ts_nosqldb/rust-db-bridge.ts +52 -4
  38. package/ts/ts_nosqldb/server/classes.nosqldbserver.ts +2 -1
  39. package/ts/ts_nosqldb/service-types.ts +4 -0
  40. package/ts/ts_nosqldb/stopped-storage.ts +108 -0
  41. package/ts/ts_nosqldb/storage-root-alignment.ts +176 -0
  42. package/ts/ts_nosqldb/storage-root-relocation.ts +9 -7
@@ -1,3 +1,18 @@
1
+ import {
2
+ normalizeStoppedStorageRootAlignmentInput, normalizeStoppedStorageRootAlignmentResult,
3
+ normalizeStoppedStorageRootAlignmentAbortResult,
4
+ type ILocalNoSqlDbStoppedStorageRootAlignmentInput, type TLocalNoSqlDbStoppedStorageRootAlignmentResult,
5
+ type ILocalNoSqlDbStoppedStorageRootAlignmentAbortResult,
6
+ } from '../ts_nosqldb/storage-root-alignment.js';
7
+ import {
8
+ NoSqlDbStoppedOperationError,
9
+ normalizeStoppedStorageRootInspectionInput,
10
+ validateNoSqlDbStoppedOperationOptions,
11
+ isStoppedOperationUnknownMethod,
12
+ type INoSqlDbStoppedOperationOptions,
13
+ type ILocalNoSqlDbStoppedStorageRootInspectionInput,
14
+ type TLocalNoSqlDbStoppedStorageRootInspection,
15
+ } from '../ts_nosqldb/stopped-storage.js';
1
16
  import * as crypto from 'node:crypto';
2
17
  import * as fs from 'node:fs/promises';
3
18
  import * as net from 'node:net';
@@ -17,6 +32,8 @@ import {
17
32
  import {
18
33
  RustDbBridge,
19
34
  rustDbBridgeStorageRootRelocation,
35
+ rustDbBridgeStorageRootAlignment,
36
+ rustDbBridgeStorageRootAlignmentAbort,
20
37
  } from '../ts_nosqldb/rust-db-bridge.js';
21
38
  import { validateNoSqlDbStorageRehearsalStartup } from '../ts_nosqldb/storage-rehearsal.js';
22
39
  import {
@@ -55,7 +72,7 @@ export interface ILocalNoSqlDbOptions {
55
72
  storageRehearsalId?: string;
56
73
  /** Exact engine executable. Invalid explicit paths reject without chmod or discovery fallback. Omit for normal discovery. */
57
74
  binaryPath?: string;
58
- /** Default: current-format-only. Native ownership precedes all file-root initialization. */
75
+ /** Default: current-format-only. existing-only refuses missing, empty or uninitialized storage. */
59
76
  fileStorageStartup?: TNoSqlDbFileStorageStartup;
60
77
  /** Required: where to store data */
61
78
  folderPath: string;
@@ -106,12 +123,12 @@ export class LocalNoSqlDb {
106
123
  };
107
124
  }
108
125
 
109
- private static createOfflineInspectionBridge(): RustDbBridge {
110
- return new RustDbBridge();
126
+ private static createOfflineInspectionBridge(binaryPath?: string): RustDbBridge {
127
+ return new RustDbBridge({ binaryPath });
111
128
  }
112
129
 
113
- private static createStorageRootRelocationBridge(): RustDbBridge {
114
- return new RustDbBridge();
130
+ private static createStorageRootRelocationBridge(binaryPath?: string): RustDbBridge {
131
+ return new RustDbBridge({ binaryPath });
115
132
  }
116
133
 
117
134
  private static async withStorageRootRelocationGate<TResult>(
@@ -148,12 +165,12 @@ export class LocalNoSqlDb {
148
165
  */
149
166
  public static async relocateStoppedStorageRoot(
150
167
  inputArg: ILocalNoSqlDbStoppedStorageRootRelocationInput,
151
- optionsArg?: INoSqlDbManagementOperationOptions,
168
+ optionsArg?: INoSqlDbStoppedOperationOptions,
152
169
  ): Promise<ILocalNoSqlDbStoppedStorageRootRelocationReceipt> {
153
170
  let input: ILocalNoSqlDbStoppedStorageRootRelocationInput;
154
171
  try {
155
172
  input = normalizeLocalNoSqlDbStoppedStorageRootRelocationInput(inputArg);
156
- validateNoSqlDbManagementOperationOptions(optionsArg);
173
+ validateNoSqlDbStoppedOperationOptions(optionsArg);
157
174
  } catch (error) {
158
175
  if (error instanceof LocalNoSqlDbStorageRootRelocationError) throw error;
159
176
  throw new LocalNoSqlDbStorageRootRelocationError('INVALID_REQUEST');
@@ -188,7 +205,7 @@ export class LocalNoSqlDb {
188
205
  let receipt: ILocalNoSqlDbStoppedStorageRootRelocationReceipt | undefined;
189
206
  let operationError: LocalNoSqlDbStorageRootRelocationError | undefined;
190
207
  try {
191
- bridge = LocalNoSqlDb.createStorageRootRelocationBridge();
208
+ bridge = LocalNoSqlDb.createStorageRootRelocationBridge(optionsArg?.binaryPath);
192
209
  LocalNoSqlDb.storageRootRelocationBridge = bridge;
193
210
  const spawnTimeout = LocalNoSqlDb.remainingManagementTimeout(deadline);
194
211
  const spawned = await bridge.spawn({
@@ -205,7 +222,9 @@ export class LocalNoSqlDb {
205
222
  });
206
223
  receipt = normalizeLocalNoSqlDbStoppedStorageRootRelocationReceipt(result, input);
207
224
  } catch (error) {
208
- if (error instanceof LocalNoSqlDbStorageRootRelocationError) {
225
+ if (error instanceof NoSqlDbStoppedOperationError || isStoppedOperationUnknownMethod(error, 'relocateStoppedStorageRoot')) {
226
+ operationError = new LocalNoSqlDbStorageRootRelocationError(error instanceof NoSqlDbStoppedOperationError ? error.code : 'UNSUPPORTED');
227
+ } else if (error instanceof LocalNoSqlDbStorageRootRelocationError) {
209
228
  operationError = error;
210
229
  } else {
211
230
  const requestWasDispatched = error instanceof Error
@@ -247,21 +266,110 @@ export class LocalNoSqlDb {
247
266
  });
248
267
  }
249
268
 
269
+ private static createStorageRootAlignmentBridge(binaryPath?: string): RustDbBridge {
270
+ return new RustDbBridge({ binaryPath });
271
+ }
272
+
273
+ /** Adopt an authority-free stopped copy as an independent provider. Retain its external receipt. */
274
+ public static alignStoppedStorageRoot(input: ILocalNoSqlDbStoppedStorageRootAlignmentInput,
275
+ options?: INoSqlDbStoppedOperationOptions): Promise<TLocalNoSqlDbStoppedStorageRootAlignmentResult> {
276
+ return LocalNoSqlDb.runStorageRootAlignment(input, options, false, normalizeStoppedStorageRootAlignmentResult);
277
+ }
278
+ /** Abort only the exact incomplete profile append; a complete append must be finished. */
279
+ public static abortStoppedStorageRootAlignment(input: ILocalNoSqlDbStoppedStorageRootAlignmentInput,
280
+ options?: INoSqlDbStoppedOperationOptions): Promise<ILocalNoSqlDbStoppedStorageRootAlignmentAbortResult> {
281
+ return LocalNoSqlDb.runStorageRootAlignment(input, options, true, normalizeStoppedStorageRootAlignmentAbortResult);
282
+ }
283
+ private static async runStorageRootAlignment<TResult>(inputArg: ILocalNoSqlDbStoppedStorageRootAlignmentInput,
284
+ options: INoSqlDbStoppedOperationOptions | undefined, abort: boolean,
285
+ normalize: (value: unknown, input: ILocalNoSqlDbStoppedStorageRootAlignmentInput) => TResult): Promise<TResult> {
286
+ let input: ILocalNoSqlDbStoppedStorageRootAlignmentInput;
287
+ try {
288
+ input = normalizeStoppedStorageRootAlignmentInput(inputArg);
289
+ validateNoSqlDbStoppedOperationOptions(options);
290
+ if (abort && input.dryRun) throw new Error('abort cannot be a dry run');
291
+ } catch { throw new NoSqlDbStoppedOperationError('INVALID_REQUEST'); }
292
+ if (options?.signal?.aborted) throw new NoSqlDbStoppedOperationError('CANCELLED');
293
+ if (!['linux', 'darwin'].includes(process.platform)) throw new NoSqlDbStoppedOperationError('UNSUPPORTED');
294
+ const deadline = Date.now() + (options?.timeoutMs ?? 3_600_000);
295
+ const remaining = () => {
296
+ if (options?.signal?.aborted) throw new NoSqlDbStoppedOperationError('CANCELLED');
297
+ const timeoutMs = deadline - Date.now();
298
+ if (timeoutMs <= 0) throw new NoSqlDbStoppedOperationError('CANCELLED');
299
+ return { signal: options?.signal, timeoutMs };
300
+ };
301
+ // Share the mutation gate and retained ownership with relocation, including
302
+ // retries after an earlier sidecar could not confirm termination.
303
+ return LocalNoSqlDb.withStorageRootRelocationGate(async () => {
304
+ const retained = LocalNoSqlDb.storageRootRelocationBridge;
305
+ if (retained) {
306
+ try { await retained.terminate({ gracePeriodMs: 0 }); }
307
+ catch { throw new NoSqlDbStoppedOperationError('RECOVERY_REQUIRED'); }
308
+ if (!retained.ownershipReleased) throw new NoSqlDbStoppedOperationError('RECOVERY_REQUIRED');
309
+ LocalNoSqlDb.storageRootRelocationBridge = undefined;
310
+ }
311
+ let bridge: RustDbBridge | undefined;
312
+ let result: TResult | undefined;
313
+ let failure: NoSqlDbStoppedOperationError | undefined;
314
+ let dispatched = false;
315
+ const method = abort ? 'abortStoppedStorageRootAlignment' : 'alignStoppedStorageRoot';
316
+ try {
317
+ remaining();
318
+ bridge = LocalNoSqlDb.createStorageRootAlignmentBridge(options?.binaryPath);
319
+ LocalNoSqlDb.storageRootRelocationBridge = bridge;
320
+ if (!await bridge.spawn(remaining())) throw new NoSqlDbStoppedOperationError('IO');
321
+ const commandOptions = remaining();
322
+ dispatched = true;
323
+ const value = abort ? await bridge[rustDbBridgeStorageRootAlignmentAbort](input, commandOptions)
324
+ : await bridge[rustDbBridgeStorageRootAlignment](input, commandOptions);
325
+ result = normalize(value, input);
326
+ } catch (error) {
327
+ if (error instanceof NoSqlDbStoppedOperationError) failure = error;
328
+ else if (isStoppedOperationUnknownMethod(error, method)) failure = new NoSqlDbStoppedOperationError('UNSUPPORTED');
329
+ else if (error instanceof Error && 'code' in error && error.code === 'ERR_RUST_BRIDGE_REQUEST_RUST_RESPONSE'
330
+ && 'method' in error && error.method === method && 'responseErrorCode' in error
331
+ && isLocalNoSqlDbStorageRootRelocationErrorCode(error.responseErrorCode)) {
332
+ failure = new NoSqlDbStoppedOperationError(error.responseErrorCode);
333
+ } else failure = new NoSqlDbStoppedOperationError(dispatched ? 'RECOVERY_REQUIRED' : 'IO');
334
+ }
335
+ if (bridge) {
336
+ try {
337
+ await bridge.terminate({ gracePeriodMs: 0 });
338
+ if (!bridge.ownershipReleased) throw new Error('termination unconfirmed');
339
+ if (LocalNoSqlDb.storageRootRelocationBridge === bridge) LocalNoSqlDb.storageRootRelocationBridge = undefined;
340
+ } catch { failure = new NoSqlDbStoppedOperationError('RECOVERY_REQUIRED'); }
341
+ }
342
+ if (failure) throw failure;
343
+ if (result === undefined) throw new NoSqlDbStoppedOperationError('RECOVERY_REQUIRED');
344
+ return result;
345
+ });
346
+ }
347
+
348
+ /** Read-only complete owner audit of a stopped source or physically admissible copy. */
349
+ public static async inspectStoppedStorageRoot(
350
+ inputArg: ILocalNoSqlDbStoppedStorageRootInspectionInput,
351
+ optionsArg?: INoSqlDbStoppedOperationOptions,
352
+ ): Promise<TLocalNoSqlDbStoppedStorageRootInspection> {
353
+ return LocalNoSqlDb.runOfflineInspection(inputArg, optionsArg,
354
+ normalizeStoppedStorageRootInspectionInput,
355
+ (bridge, input, options) => bridge.inspectStoppedStorageRoot(input, options));
356
+ }
357
+
250
358
  private static async runOfflineInspection<
251
359
  TInput extends { folderPath: string },
252
360
  TResult,
253
361
  >(
254
362
  inputArg: TInput,
255
- optionsArg: INoSqlDbManagementOperationOptions | undefined,
363
+ optionsArg: INoSqlDbStoppedOperationOptions | undefined,
256
364
  normalizeInputArg: (inputArg: TInput) => TInput,
257
365
  inspectArg: (
258
366
  bridgeArg: RustDbBridge,
259
367
  inputArg: TInput,
260
- optionsArg?: INoSqlDbManagementOperationOptions,
368
+ optionsArg?: INoSqlDbStoppedOperationOptions,
261
369
  ) => Promise<TResult>,
262
370
  ): Promise<TResult> {
263
371
  const validatedInput = normalizeInputArg(inputArg);
264
- validateNoSqlDbManagementOperationOptions(optionsArg);
372
+ validateNoSqlDbStoppedOperationOptions(optionsArg);
265
373
  optionsArg?.signal?.throwIfAborted();
266
374
 
267
375
  const input = normalizeInputArg({
@@ -271,7 +379,7 @@ export class LocalNoSqlDb {
271
379
  const deadline = optionsArg?.timeoutMs === undefined
272
380
  ? undefined
273
381
  : Date.now() + optionsArg.timeoutMs;
274
- const bridge = LocalNoSqlDb.createOfflineInspectionBridge();
382
+ const bridge = LocalNoSqlDb.createOfflineInspectionBridge(optionsArg?.binaryPath);
275
383
  let operationFailed = false;
276
384
  let operationError: unknown;
277
385
  let result: TResult | undefined;
@@ -334,7 +442,7 @@ export class LocalNoSqlDb {
334
442
  */
335
443
  public static async inspectOfflinePhysicalNamespaces(
336
444
  inputArg: ILocalNoSqlDbOfflinePhysicalNamespaceInspection,
337
- optionsArg?: INoSqlDbManagementOperationOptions,
445
+ optionsArg?: INoSqlDbStoppedOperationOptions,
338
446
  ): Promise<ILocalNoSqlDbOfflinePhysicalNamespaceInspectionResult> {
339
447
  return LocalNoSqlDb.runOfflineInspection(
340
448
  inputArg,
@@ -351,7 +459,7 @@ export class LocalNoSqlDb {
351
459
  */
352
460
  public static async inspectOfflineStringValue(
353
461
  inputArg: ILocalNoSqlDbOfflineStringValueInspection,
354
- optionsArg?: INoSqlDbManagementOperationOptions,
462
+ optionsArg?: INoSqlDbStoppedOperationOptions,
355
463
  ): Promise<TLocalNoSqlDbOfflineStringValueInspectionResult> {
356
464
  return LocalNoSqlDb.runOfflineInspection(
357
465
  inputArg,
@@ -1,9 +1,10 @@
1
1
  /** Native format admission is read-only until the engine owns the file root. */
2
- export type TNoSqlDbFileStorageStartup = 'current-format-only';
2
+ export type TNoSqlDbFileStorageStartup = 'current-format-only' | 'existing-only';
3
3
 
4
4
  export const normalizeNoSqlDbFileStorageStartup = (
5
5
  valueArg: unknown,
6
6
  ): TNoSqlDbFileStorageStartup => {
7
7
  if (valueArg === undefined || valueArg === 'current-format-only') return 'current-format-only';
8
- throw new TypeError('NoSQLDB fileStorageStartup must be "current-format-only"; automatic storage conversion has been removed');
8
+ if (valueArg === 'existing-only') return 'existing-only';
9
+ throw new TypeError('NoSQLDB fileStorageStartup must be "current-format-only" or "existing-only"; automatic storage conversion has been removed');
9
10
  };
@@ -1,4 +1,11 @@
1
1
  // NoSQLDB - Wire protocol compatible embedded database server
2
+ export { NoSqlDbStoppedOperationError } from './stopped-storage.js';
3
+ export type {
4
+ INoSqlDbStoppedOperationOptions, ILocalNoSqlDbStoppedStorageRootInspectionInput,
5
+ TLocalNoSqlDbStoppedStorageRootInspection, ILocalNoSqlDbAuditedStorageRoot,
6
+ ILocalNoSqlDbUnavailableStorageRoot, INoSqlDbPhysicalAdmissionViolation,
7
+ TNoSqlDbStorageRootBindingChange,
8
+ } from './stopped-storage.js';
2
9
  export { NoSqlDbStorageRehearsal } from './classes.storagerehearsal.js';
3
10
  export { NoSqlDbStorageRehearsalError, noSqlDbStorageRehearsalErrorCodes } from './storage-rehearsal.js';
4
11
  export type {
@@ -102,3 +109,9 @@ export {
102
109
  } from './storage-root-relocation.js';
103
110
  export type { TNoSqlDbEffectivePayloadInputV1 } from './resource-fencing.js';
104
111
  export { createNoSqlDbEffectivePayloadSha256V1 } from './resource-fencing.js';
112
+
113
+ export type {
114
+ INoSqlDbStorageRootPins, ILocalNoSqlDbStoppedStorageRootAlignmentInput,
115
+ ILocalNoSqlDbStoppedStorageRootAlignmentReceipt, TLocalNoSqlDbStoppedStorageRootAlignmentResult,
116
+ ILocalNoSqlDbStoppedStorageRootAlignmentAbortResult,
117
+ } from './storage-root-alignment.js';
@@ -1,5 +1,8 @@
1
+ import { normalizeStoppedStorageRootAlignmentInput, type ILocalNoSqlDbStoppedStorageRootAlignmentInput, type TLocalNoSqlDbStoppedStorageRootAlignmentResult, type ILocalNoSqlDbStoppedStorageRootAlignmentAbortResult } from './storage-root-alignment.js';
2
+ import { normalizeStoppedStorageRootInspectionInput, normalizeStoppedOperationUnknownMethod, type ILocalNoSqlDbStoppedStorageRootInspectionInput, type TLocalNoSqlDbStoppedStorageRootInspection } from './stopped-storage.js';
1
3
  import * as plugins from './plugins.js';
2
4
  import { normalizeNoSqlDbFileStorageOptions } from './file-storage-options.js';
5
+ import type { TNoSqlDbFileStorageStartup } from './file-storage-startup.js';
3
6
  import * as path from 'node:path';
4
7
  import * as url from 'node:url';
5
8
  import { EventEmitter } from 'node:events';
@@ -192,7 +195,16 @@ type TOfflinePhysicalNamespaceInspectionCommandParams = {
192
195
  timeoutMs?: number;
193
196
  } & ILocalNoSqlDbOfflinePhysicalNamespaceInspection['limits'];
194
197
 
198
+ export const rustDbBridgeStorageRootAlignment = Symbol('storageRootAlignment');
199
+ export const rustDbBridgeStorageRootAlignmentAbort = Symbol('storageRootAlignmentAbort');
200
+
195
201
  type TNoSqlDbCommands = {
202
+ alignStoppedStorageRoot: { params: ILocalNoSqlDbStoppedStorageRootAlignmentInput & { timeoutMs?: number }; result: TLocalNoSqlDbStoppedStorageRootAlignmentResult };
203
+ abortStoppedStorageRootAlignment: { params: ILocalNoSqlDbStoppedStorageRootAlignmentInput & { timeoutMs?: number }; result: ILocalNoSqlDbStoppedStorageRootAlignmentAbortResult };
204
+ inspectStoppedStorageRoot: {
205
+ params: ILocalNoSqlDbStoppedStorageRootInspectionInput & { timeoutMs?: number };
206
+ result: TLocalNoSqlDbStoppedStorageRootInspection;
207
+ };
196
208
  createStoppedStorageRehearsal: {
197
209
  params: { request: TNormalizedNoSqlDbStorageRehearsalInput; timeoutMs: number };
198
210
  result: INoSqlDbStorageRehearsalReceipt;
@@ -311,6 +323,7 @@ export interface INoSqlDbQueryOptions {
311
323
  }
312
324
 
313
325
  interface INoSqlDbRustConfig {
326
+ fileStorageStartup?: TNoSqlDbFileStorageStartup;
314
327
  storageRehearsalId?: string;
315
328
  port?: number;
316
329
  host?: string;
@@ -610,7 +623,7 @@ export class RustDbBridge extends EventEmitter {
610
623
  { request, timeoutMs: optionsArg.timeoutMs }, optionsArg) as INoSqlDbStorageRehearsalReceipt;
611
624
  } catch (error) {
612
625
  await this.awaitManagementOperationTermination(error);
613
- throw error;
626
+ throw normalizeStoppedOperationUnknownMethod(error, 'createStoppedStorageRehearsal');
614
627
  }
615
628
  }
616
629
 
@@ -628,7 +641,42 @@ export class RustDbBridge extends EventEmitter {
628
641
  ) as ILocalNoSqlDbStoppedStorageRootRelocationReceipt;
629
642
  } catch (error) {
630
643
  await this.awaitManagementOperationTermination(error);
631
- throw error;
644
+ throw normalizeStoppedOperationUnknownMethod(error, 'relocateStoppedStorageRoot');
645
+ }
646
+ }
647
+
648
+ public async [rustDbBridgeStorageRootAlignment](inputArg: ILocalNoSqlDbStoppedStorageRootAlignmentInput,
649
+ optionsArg?: INoSqlDbManagementOperationOptions): Promise<TLocalNoSqlDbStoppedStorageRootAlignmentResult> {
650
+ return this.stoppedAlignmentCommand('alignStoppedStorageRoot', inputArg, optionsArg) as Promise<TLocalNoSqlDbStoppedStorageRootAlignmentResult>;
651
+ }
652
+ public async [rustDbBridgeStorageRootAlignmentAbort](inputArg: ILocalNoSqlDbStoppedStorageRootAlignmentInput,
653
+ optionsArg?: INoSqlDbManagementOperationOptions): Promise<ILocalNoSqlDbStoppedStorageRootAlignmentAbortResult> {
654
+ return this.stoppedAlignmentCommand('abortStoppedStorageRootAlignment', inputArg, optionsArg) as Promise<ILocalNoSqlDbStoppedStorageRootAlignmentAbortResult>;
655
+ }
656
+ private async stoppedAlignmentCommand(method: 'alignStoppedStorageRoot' | 'abortStoppedStorageRootAlignment',
657
+ inputArg: ILocalNoSqlDbStoppedStorageRootAlignmentInput, optionsArg?: INoSqlDbManagementOperationOptions) {
658
+ const input = normalizeStoppedStorageRootAlignmentInput(inputArg);
659
+ validateNoSqlDbManagementOperationOptions(optionsArg);
660
+ try {
661
+ return await this.bridge.sendCommand(method, { ...input, timeoutMs: optionsArg?.timeoutMs }, optionsArg);
662
+ } catch (error) {
663
+ await this.awaitManagementOperationTermination(error);
664
+ throw normalizeStoppedOperationUnknownMethod(error, method);
665
+ }
666
+ }
667
+
668
+ public async inspectStoppedStorageRoot(
669
+ inputArg: ILocalNoSqlDbStoppedStorageRootInspectionInput,
670
+ optionsArg?: INoSqlDbManagementOperationOptions,
671
+ ): Promise<TLocalNoSqlDbStoppedStorageRootInspection> {
672
+ const input = normalizeStoppedStorageRootInspectionInput(inputArg);
673
+ validateNoSqlDbManagementOperationOptions(optionsArg);
674
+ try {
675
+ return await this.bridge.sendCommand('inspectStoppedStorageRoot',
676
+ { ...input, timeoutMs: optionsArg?.timeoutMs }, optionsArg) as TLocalNoSqlDbStoppedStorageRootInspection;
677
+ } catch (error) {
678
+ await this.awaitManagementOperationTermination(error);
679
+ throw normalizeStoppedOperationUnknownMethod(error, 'inspectStoppedStorageRoot');
632
680
  }
633
681
  }
634
682
 
@@ -651,7 +699,7 @@ export class RustDbBridge extends EventEmitter {
651
699
  ) as ILocalNoSqlDbOfflinePhysicalNamespaceInspectionResult;
652
700
  } catch (error) {
653
701
  await this.awaitManagementOperationTermination(error);
654
- throw error;
702
+ throw normalizeStoppedOperationUnknownMethod(error, 'inspectOfflinePhysicalNamespaces');
655
703
  }
656
704
  }
657
705
 
@@ -669,7 +717,7 @@ export class RustDbBridge extends EventEmitter {
669
717
  ) as TLocalNoSqlDbOfflineStringValueInspectionResult;
670
718
  } catch (error) {
671
719
  await this.awaitManagementOperationTermination(error);
672
- throw error;
720
+ throw normalizeStoppedOperationUnknownMethod(error, 'inspectOfflineStringValue');
673
721
  }
674
722
  }
675
723
 
@@ -58,7 +58,7 @@ export interface INoSqlDbServerOptions {
58
58
  binaryPath?: string;
59
59
  /** Exact rehearsal operation ID. Requires authenticated current-format-only file storage and an explicit private Unix socket. Never promotes a rehearsal. */
60
60
  storageRehearsalId?: string;
61
- /** Default: current-format-only. The native owner rejects foreign, mixed and migration staging roots without conversion. */
61
+ /** Default: current-format-only. existing-only also requires initialized storage and refuses fresh creation. */
62
62
  fileStorageStartup?: TNoSqlDbFileStorageStartup;
63
63
  /** Port to listen on (default: 27017, 0 requests an OS-assigned port) - ignored if socketPath is set */
64
64
  port?: number;
@@ -261,6 +261,7 @@ export class NoSqlDbServer {
261
261
  socketPath: this.options.socketPath,
262
262
  storage: this.options.storage ?? 'memory',
263
263
  storagePath: this.options.storagePath,
264
+ ...(this.options.fileStorageStartup === 'existing-only' ? { fileStorageStartup: 'existing-only' as const } : {}),
264
265
  storageRehearsalId: this.options.storageRehearsalId,
265
266
  auth: this.options.auth,
266
267
  tls: this.options.tls,
@@ -1,4 +1,7 @@
1
1
  export interface INoSqlDbHealth {
2
+ /** Present on engines supporting stopped root alignment. */
3
+ providerRootId?: string;
4
+ lsn?: string | null;
2
5
  running: boolean;
3
6
  storage?: 'memory' | 'file';
4
7
  fileStorageEngine?: 'mvcc';
@@ -271,6 +274,7 @@ export interface INoSqlDbManagementOperationOptions {
271
274
  }
272
275
 
273
276
  export interface INoSqlDbDatabaseResourceFenceState {
277
+ providerRootId?: string;
274
278
  version: 1;
275
279
  databaseName: string;
276
280
  resourceIdSha256: string;
@@ -0,0 +1,108 @@
1
+ import * as plugins from './plugins.js';
2
+ import { validateNoSqlDbManagementOperationOptions } from './offline-inspection.js';
3
+ import type { INoSqlDbManagementOperationOptions } from './service-types.js';
4
+ import type { TLocalNoSqlDbStorageRootRelocationErrorCode } from './storage-root-relocation.js';
5
+
6
+ export interface INoSqlDbStoppedOperationOptions extends INoSqlDbManagementOperationOptions {
7
+ /** Exact executable; invalid explicit paths never fall back to discovery. */
8
+ binaryPath?: string;
9
+ }
10
+
11
+ export interface ILocalNoSqlDbStoppedStorageRootInspectionInput { folderPath: string; }
12
+ export type TNoSqlDbStorageRootBindingChange = 'none' | 'path' | 'inode' | 'path+inode';
13
+ export interface INoSqlDbPhysicalAdmissionViolation {
14
+ relativePath: string;
15
+ rule: string;
16
+ observed: string;
17
+ }
18
+ export interface ILocalNoSqlDbAuditedStorageRoot {
19
+ status: 'audited';
20
+ bindingState: 'matching' | 'foreign';
21
+ bindingChange: TNoSqlDbStorageRootBindingChange;
22
+ storagePurpose: 'production';
23
+ profileSchema: 1 | 4;
24
+ providerRootId: string;
25
+ /** Group/timeline describe shared lineage, not independent provider identity. */
26
+ groupId: string;
27
+ timelineId: string;
28
+ /** All counts and positions are canonical unsigned decimal strings. */
29
+ lsn: string;
30
+ records: string;
31
+ recordsSha256: string;
32
+ recordsWithoutFencingProfileSha256: string;
33
+ authUsers: string;
34
+ authority: {
35
+ resourceFences: string;
36
+ receipts: string;
37
+ publicationHolds: string;
38
+ activeAllocations: string;
39
+ retiredAllocations: string;
40
+ allocationReplays: string;
41
+ activeGrants: '0';
42
+ importStages: '0';
43
+ };
44
+ generations: { manifest: string; onDisk: string[] };
45
+ alignmentEligible: boolean;
46
+ reasons: string[];
47
+ physicalAdmissionViolations: [];
48
+ }
49
+ export interface ILocalNoSqlDbUnavailableStorageRoot {
50
+ status: 'unavailable';
51
+ stage: 'admission' | 'audit';
52
+ alignmentEligible: false;
53
+ reasons: string[];
54
+ /** Bounded advisory evidence. No logical identity or pin is trusted here. */
55
+ physicalAdmissionViolations: INoSqlDbPhysicalAdmissionViolation[];
56
+ }
57
+ export type TLocalNoSqlDbStoppedStorageRootInspection =
58
+ ILocalNoSqlDbAuditedStorageRoot | ILocalNoSqlDbUnavailableStorageRoot;
59
+
60
+ export class NoSqlDbStoppedOperationError extends Error {
61
+ public readonly responseErrorCode: TLocalNoSqlDbStorageRootRelocationErrorCode;
62
+ public constructor(public readonly code: TLocalNoSqlDbStorageRootRelocationErrorCode) {
63
+ super(`NoSQLDB stopped operation failed with code ${code}`);
64
+ this.name = 'NoSqlDbStoppedOperationError';
65
+ this.responseErrorCode = code;
66
+ }
67
+ }
68
+
69
+ export const validateNoSqlDbStoppedOperationOptions = (options?: INoSqlDbStoppedOperationOptions): void => {
70
+ validateNoSqlDbManagementOperationOptions(options);
71
+ if (options?.binaryPath !== undefined && (typeof options.binaryPath !== 'string'
72
+ || !options.binaryPath.length || options.binaryPath.includes('\0'))) {
73
+ throw new NoSqlDbStoppedOperationError('INVALID_REQUEST');
74
+ }
75
+ };
76
+
77
+ export const normalizeStoppedStorageRootInspectionInput = (
78
+ input: ILocalNoSqlDbStoppedStorageRootInspectionInput,
79
+ ): ILocalNoSqlDbStoppedStorageRootInspectionInput => {
80
+ if (!input || Object.getPrototypeOf(input) !== Object.prototype
81
+ || Reflect.ownKeys(input).length !== 1
82
+ || !Object.getOwnPropertyDescriptor(input, 'folderPath')?.enumerable
83
+ || !('value' in Object.getOwnPropertyDescriptor(input, 'folderPath')!)) {
84
+ throw new NoSqlDbStoppedOperationError('INVALID_REQUEST');
85
+ }
86
+ const folderPath = input.folderPath;
87
+ if (typeof folderPath !== 'string' || plugins.buffer.Buffer.byteLength(folderPath) > 4095
88
+ || !plugins.path.isAbsolute(folderPath) || plugins.path.dirname(folderPath) === folderPath
89
+ || folderPath.endsWith('/') || folderPath.includes('//') || /\p{Cc}/u.test(folderPath)
90
+ || folderPath.split('/').some(part => part === '.' || part === '..' || part.startsWith('.smartdb-rehearsal-'))) {
91
+ throw new NoSqlDbStoppedOperationError('INVALID_REQUEST');
92
+ }
93
+ return { folderPath };
94
+ };
95
+
96
+ /** Only a confirmed Rust response for this exact command proves non-dispatch.
97
+ * Transport errors with the same text remain ambiguous. */
98
+ export const isStoppedOperationUnknownMethod = (error: unknown, method: string): boolean =>
99
+ error instanceof Error && 'code' in error
100
+ && error.code === 'ERR_RUST_BRIDGE_REQUEST_RUST_RESPONSE'
101
+ && 'requestId' in error && typeof error.requestId === 'string' && error.requestId.length > 0
102
+ && 'method' in error && error.method === method
103
+ && (('responseErrorCode' in error && error.responseErrorCode === 'UNKNOWN_METHOD')
104
+ || ((!('responseErrorCode' in error) || error.responseErrorCode === undefined)
105
+ && error.message === `Unknown method: ${method}`));
106
+
107
+ export const normalizeStoppedOperationUnknownMethod = (error: unknown, method: string): unknown =>
108
+ isStoppedOperationUnknownMethod(error, method) ? new NoSqlDbStoppedOperationError('UNSUPPORTED') : error;