@lossless.org/nosqldb 8.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.
Files changed (81) hide show
  1. package/.smartconfig.json +107 -0
  2. package/dist_rust/rustdb_linux_amd64 +0 -0
  3. package/dist_rust/rustdb_linux_amd64.tsrust-build.json +14 -0
  4. package/dist_rust/rustdb_linux_arm64 +0 -0
  5. package/dist_rust/rustdb_linux_arm64.tsrust-build.json +14 -0
  6. package/dist_rust/rustdb_macos_amd64 +0 -0
  7. package/dist_rust/rustdb_macos_amd64.tsrust-build.json +14 -0
  8. package/dist_rust/rustdb_macos_arm64 +0 -0
  9. package/dist_rust/rustdb_macos_arm64.tsrust-build.json +14 -0
  10. package/dist_ts/00_commitinfo_data.d.ts +8 -0
  11. package/dist_ts/00_commitinfo_data.js +9 -0
  12. package/dist_ts/index.d.ts +8 -0
  13. package/dist_ts/index.js +10 -0
  14. package/dist_ts/ts_local/classes.localnosqldb.d.ts +119 -0
  15. package/dist_ts/ts_local/classes.localnosqldb.js +386 -0
  16. package/dist_ts/ts_local/index.d.ts +5 -0
  17. package/dist_ts/ts_local/index.js +3 -0
  18. package/dist_ts/ts_local/plugins.d.ts +1 -0
  19. package/dist_ts/ts_local/plugins.js +3 -0
  20. package/dist_ts/ts_nosqldb/classes.storagerehearsal.d.ts +19 -0
  21. package/dist_ts/ts_nosqldb/classes.storagerehearsal.js +122 -0
  22. package/dist_ts/ts_nosqldb/file-storage-options.d.ts +13 -0
  23. package/dist_ts/ts_nosqldb/file-storage-options.js +24 -0
  24. package/dist_ts/ts_nosqldb/file-storage-startup.d.ts +3 -0
  25. package/dist_ts/ts_nosqldb/file-storage-startup.js +6 -0
  26. package/dist_ts/ts_nosqldb/index.d.ts +14 -0
  27. package/dist_ts/ts_nosqldb/index.js +11 -0
  28. package/dist_ts/ts_nosqldb/offline-inspection.d.ts +72 -0
  29. package/dist_ts/ts_nosqldb/offline-inspection.js +282 -0
  30. package/dist_ts/ts_nosqldb/plugins.d.ts +7 -0
  31. package/dist_ts/ts_nosqldb/plugins.js +10 -0
  32. package/dist_ts/ts_nosqldb/resource-fencing.d.ts +39 -0
  33. package/dist_ts/ts_nosqldb/resource-fencing.js +271 -0
  34. package/dist_ts/ts_nosqldb/rust-db-bridge.d.ts +213 -0
  35. package/dist_ts/ts_nosqldb/rust-db-bridge.js +439 -0
  36. package/dist_ts/ts_nosqldb/server/classes.nosqldbserver.d.ts +204 -0
  37. package/dist_ts/ts_nosqldb/server/classes.nosqldbserver.js +456 -0
  38. package/dist_ts/ts_nosqldb/server/index.d.ts +3 -0
  39. package/dist_ts/ts_nosqldb/server/index.js +3 -0
  40. package/dist_ts/ts_nosqldb/service-types.d.ts +341 -0
  41. package/dist_ts/ts_nosqldb/service-types.js +62 -0
  42. package/dist_ts/ts_nosqldb/storage-rehearsal.d.ts +103 -0
  43. package/dist_ts/ts_nosqldb/storage-rehearsal.js +131 -0
  44. package/dist_ts/ts_nosqldb/storage-root-relocation.d.ts +27 -0
  45. package/dist_ts/ts_nosqldb/storage-root-relocation.js +211 -0
  46. package/dist_ts_debugserver/bundled.d.ts +4 -0
  47. package/dist_ts_debugserver/bundled.js +16 -0
  48. package/dist_ts_debugserver/classes.debugserver.d.ts +36 -0
  49. package/dist_ts_debugserver/classes.debugserver.js +95 -0
  50. package/dist_ts_debugserver/index.d.ts +2 -0
  51. package/dist_ts_debugserver/index.js +2 -0
  52. package/dist_ts_debugserver/plugins.d.ts +2 -0
  53. package/dist_ts_debugserver/plugins.js +3 -0
  54. package/dist_ts_debugui/index.d.ts +2 -0
  55. package/dist_ts_debugui/index.js +2 -0
  56. package/dist_ts_debugui/nosqldb-debugui.d.ts +62 -0
  57. package/dist_ts_debugui/nosqldb-debugui.js +1132 -0
  58. package/dist_ts_debugui/plugins.d.ts +1 -0
  59. package/dist_ts_debugui/plugins.js +2 -0
  60. package/license +21 -0
  61. package/package.json +82 -0
  62. package/readme.md +1733 -0
  63. package/third-party-notices.md +925 -0
  64. package/ts/00_commitinfo_data.ts +8 -0
  65. package/ts/index.ts +85 -0
  66. package/ts/ts_local/classes.localnosqldb.ts +500 -0
  67. package/ts/ts_local/index.ts +19 -0
  68. package/ts/ts_local/plugins.ts +1 -0
  69. package/ts/ts_nosqldb/classes.storagerehearsal.ts +109 -0
  70. package/ts/ts_nosqldb/file-storage-options.ts +33 -0
  71. package/ts/ts_nosqldb/file-storage-startup.ts +9 -0
  72. package/ts/ts_nosqldb/index.ts +104 -0
  73. package/ts/ts_nosqldb/offline-inspection.ts +375 -0
  74. package/ts/ts_nosqldb/plugins.ts +12 -0
  75. package/ts/ts_nosqldb/resource-fencing.ts +368 -0
  76. package/ts/ts_nosqldb/rust-db-bridge.ts +964 -0
  77. package/ts/ts_nosqldb/server/classes.nosqldbserver.ts +696 -0
  78. package/ts/ts_nosqldb/server/index.ts +4 -0
  79. package/ts/ts_nosqldb/service-types.ts +484 -0
  80. package/ts/ts_nosqldb/storage-rehearsal.ts +215 -0
  81. package/ts/ts_nosqldb/storage-root-relocation.ts +277 -0
@@ -0,0 +1,8 @@
1
+ /**
2
+ * autocreated commitinfo by @push.rocks/commitinfo
3
+ */
4
+ export const commitinfo = {
5
+ name: '@lossless.org/nosqldb',
6
+ version: '8.0.0',
7
+ description: 'A MongoDB-compatible embedded database server with wire protocol support, backed by a high-performance Rust engine.'
8
+ }
package/ts/index.ts ADDED
@@ -0,0 +1,85 @@
1
+ import { commitinfo } from './00_commitinfo_data.js';
2
+
3
+ // Export core NoSQLDB module
4
+ export * from './ts_nosqldb/index.js';
5
+
6
+ // Export LocalNoSqlDb
7
+ export { LocalNoSqlDb } from './ts_local/index.js';
8
+ export type {
9
+ ILocalNoSqlDbOptions,
10
+ ILocalNoSqlDbConnectionInfo,
11
+ ILocalNoSqlDbOfflineInspectionLimits,
12
+ ILocalNoSqlDbOfflinePhysicalNamespaceInspectionLimits,
13
+ ILocalNoSqlDbOfflinePhysicalNamespaceInspection,
14
+ ILocalNoSqlDbOfflinePhysicalNamespaceInspectionResult,
15
+ ILocalNoSqlDbOfflineStringValueInspection,
16
+ TLocalNoSqlDbOfflineStringValueInspectionResult,
17
+ ILocalNoSqlDbStoppedStorageRootRelocationInput,
18
+ ILocalNoSqlDbStoppedStorageRootRelocationReceipt,
19
+ TLocalNoSqlDbStorageRootRelocationErrorCode,
20
+ } from './ts_local/index.js';
21
+ export {
22
+ LocalNoSqlDbStorageRootRelocationError,
23
+ localNoSqlDbStorageRootRelocationErrorCodes,
24
+ } from './ts_local/index.js';
25
+
26
+ // Export commitinfo
27
+ export { commitinfo };
28
+
29
+ // Re-export oplog / debug types for convenience
30
+ export type {
31
+ IOpLogEntry,
32
+ IOpLogResult,
33
+ IOpLogStats,
34
+ IRevertResult,
35
+ ICollectionInfo,
36
+ IDocumentsResult,
37
+ INoSqlDbMetrics,
38
+ INoSqlDbHealth,
39
+ INoSqlDbResourceFence,
40
+ INoSqlDbResourceFenceReceipt,
41
+ INoSqlDbHeldPublicationReceipt,
42
+ INoSqlDbCommitDatabasePublicationInput,
43
+ INoSqlDbManagementOperationOptions,
44
+ INoSqlDbDatabaseResourceFenceState,
45
+ INoSqlDbDatabaseAllocationIdentity,
46
+ INoSqlDbDatabaseAllocationLifecycle,
47
+ INoSqlDbDatabaseAllocationReadAccessGrantIdentity,
48
+ INoSqlDbIssueDatabaseAllocationReadAccessGrantInput,
49
+ INoSqlDbIssueDatabaseAllocationReadAccessGrantResult,
50
+ INoSqlDbRevokeDatabaseAllocationReadAccessGrantInput,
51
+ INoSqlDbRevokeDatabaseAllocationReadAccessGrantResult,
52
+ TNoSqlDbCommitDatabasePublicationResult,
53
+ TNoSqlDbResourceFenceErrorCode,
54
+ TNoSqlDbAllocationReadAccessGrantErrorCode,
55
+ INoSqlDbDatabaseTenantInput,
56
+ INoSqlDbAttestDatabaseTenantInput,
57
+ INoSqlDbAttestDatabaseTenantResult,
58
+ INoSqlDbEnsureDatabaseTenantInput,
59
+ INoSqlDbEnsureDatabaseTenantResult,
60
+ INoSqlDbAllocateDatabaseTenantInput,
61
+ INoSqlDbAllocateDatabaseTenantResult,
62
+ INoSqlDbDeleteDatabaseTenantInput,
63
+ INoSqlDbRotateDatabaseTenantPasswordInput,
64
+ INoSqlDbDatabaseTenantDescriptor,
65
+ INoSqlDbDeleteDatabaseTenantResult,
66
+ INoSqlDbDatabaseExportCollection,
67
+ INoSqlDbDatabaseExport,
68
+ INoSqlDbDatabaseExportLimits,
69
+ INoSqlDbExportDatabaseInput,
70
+ INoSqlDbDatabaseContentDigestLimits,
71
+ INoSqlDbGetDatabaseContentDigestInput,
72
+ INoSqlDbDatabaseContentDigest,
73
+ INoSqlDbImportDatabaseInput,
74
+ INoSqlDbImportDatabaseResult,
75
+ TNoSqlDbEffectivePayloadInputV1,
76
+ } from './ts_nosqldb/index.js';
77
+ export {
78
+ NoSqlDbAllocationReadAccessGrantError,
79
+ normalizeNoSqlDbAllocationReadAccessGrantError,
80
+ noSqlDbAllocationReadAccessGrantErrorCodes,
81
+ NoSqlDbResourceFenceError,
82
+ normalizeNoSqlDbResourceFenceError,
83
+ noSqlDbResourceFenceErrorCodes,
84
+ createNoSqlDbEffectivePayloadSha256V1,
85
+ } from './ts_nosqldb/index.js';
@@ -0,0 +1,500 @@
1
+ import * as crypto from 'crypto';
2
+ import * as fs from 'fs/promises';
3
+ import * as net from 'net';
4
+ import * as path from 'path';
5
+ import * as os from 'os';
6
+ import {
7
+ NoSqlDbServer,
8
+ type INoSqlDbAuthOptions,
9
+ type ILocalNoSqlDbOfflinePhysicalNamespaceInspection,
10
+ type ILocalNoSqlDbOfflinePhysicalNamespaceInspectionResult,
11
+ type ILocalNoSqlDbStoppedStorageRootRelocationInput,
12
+ type ILocalNoSqlDbStoppedStorageRootRelocationReceipt,
13
+ type ILocalNoSqlDbOfflineStringValueInspection,
14
+ type INoSqlDbManagementOperationOptions,
15
+ type TLocalNoSqlDbOfflineStringValueInspectionResult,
16
+ } from '../ts_nosqldb/index.js';
17
+ import {
18
+ RustDbBridge,
19
+ rustDbBridgeStorageRootRelocation,
20
+ } from '../ts_nosqldb/rust-db-bridge.js';
21
+ import { validateNoSqlDbStorageRehearsalStartup } from '../ts_nosqldb/storage-rehearsal.js';
22
+ import {
23
+ type TNoSqlDbFileStorageStartup,
24
+ } from '../ts_nosqldb/file-storage-startup.js';
25
+ import {
26
+ normalizeNoSqlDbFileStorageOptions,
27
+ } from '../ts_nosqldb/file-storage-options.js';
28
+ import {
29
+ normalizeLocalNoSqlDbOfflinePhysicalNamespaceInspectionInput,
30
+ normalizeLocalNoSqlDbOfflineInspectionInput,
31
+ validateNoSqlDbManagementOperationOptions,
32
+ } from '../ts_nosqldb/offline-inspection.js';
33
+ import {
34
+ assertNoLocalNoSqlDbStorageRootRelocationWorkspace,
35
+ isLocalNoSqlDbStorageRootRelocationErrorCode,
36
+ LocalNoSqlDbStorageRootRelocationError,
37
+ normalizeLocalNoSqlDbStoppedStorageRootRelocationInput,
38
+ normalizeLocalNoSqlDbStoppedStorageRootRelocationReceipt,
39
+ } from '../ts_nosqldb/storage-root-relocation.js';
40
+
41
+ /**
42
+ * Connection information returned by LocalNoSqlDb.start()
43
+ */
44
+ export interface ILocalNoSqlDbConnectionInfo {
45
+ /** The Unix socket file path */
46
+ socketPath: string;
47
+ /** Connection URI (mongodb:// scheme) ready for MongoClient */
48
+ connectionUri: string;
49
+ }
50
+
51
+ export interface ILocalNoSqlDbOptions {
52
+ /** Persisted authentication, including the source-verbatim credentials of a rehearsal destination. */
53
+ auth?: INoSqlDbAuthOptions;
54
+ /** Exact rehearsal operation ID; requires current-format-only startup and an explicit private socket. */
55
+ storageRehearsalId?: string;
56
+ /** Exact engine executable. Invalid explicit paths reject without chmod or discovery fallback. Omit for normal discovery. */
57
+ binaryPath?: string;
58
+ /** Default: current-format-only. Native ownership precedes all file-root initialization. */
59
+ fileStorageStartup?: TNoSqlDbFileStorageStartup;
60
+ /** Required: where to store data */
61
+ folderPath: string;
62
+ /** Optional: custom socket path (default: auto-generated in /tmp) */
63
+ socketPath?: string;
64
+ }
65
+
66
+ /**
67
+ * LocalNoSqlDb - Lightweight local MongoDB-compatible database using Unix sockets
68
+ *
69
+ * This class wraps NoSqlDbServer and provides a simple interface for
70
+ * starting a local file-based database server. Returns connection
71
+ * info that you can use with any compatible driver instance.
72
+ *
73
+ * @example
74
+ * ```typescript
75
+ * import { LocalNoSqlDb } from '@lossless.org/nosqldb';
76
+ * import { MongoClient } from 'mongodb';
77
+ *
78
+ * const db = new LocalNoSqlDb({ folderPath: './data' });
79
+ * const { connectionUri } = await db.start();
80
+ *
81
+ * // Connect with the driver
82
+ * const client = new MongoClient(connectionUri, { directConnection: true });
83
+ * await client.connect();
84
+ *
85
+ * // Use the client
86
+ * const collection = client.db('mydb').collection('users');
87
+ * await collection.insertOne({ name: 'Alice' });
88
+ *
89
+ * // When done
90
+ * await client.close();
91
+ * await db.stop();
92
+ * ```
93
+ */
94
+ export class LocalNoSqlDb {
95
+ private static storageRootRelocationGate: Promise<void> = Promise.resolve();
96
+ private static storageRootRelocationBridge: RustDbBridge | undefined;
97
+ private options: ILocalNoSqlDbOptions;
98
+ private server: NoSqlDbServer | null = null;
99
+ private generatedSocketPath: string | null = null;
100
+
101
+ constructor(options: ILocalNoSqlDbOptions) {
102
+ validateNoSqlDbStorageRehearsalStartup({ ...options, storage: 'file', storagePath: options.folderPath });
103
+ this.options = {
104
+ ...options,
105
+ ...normalizeNoSqlDbFileStorageOptions({ ...options, storage: 'file' }),
106
+ };
107
+ }
108
+
109
+ private static createOfflineInspectionBridge(): RustDbBridge {
110
+ return new RustDbBridge();
111
+ }
112
+
113
+ private static createStorageRootRelocationBridge(): RustDbBridge {
114
+ return new RustDbBridge();
115
+ }
116
+
117
+ private static async withStorageRootRelocationGate<TResult>(
118
+ operationArg: () => Promise<TResult>,
119
+ ): Promise<TResult> {
120
+ const previous = LocalNoSqlDb.storageRootRelocationGate;
121
+ let release!: () => void;
122
+ LocalNoSqlDb.storageRootRelocationGate = new Promise<void>((resolveArg) => {
123
+ release = resolveArg;
124
+ });
125
+ await previous;
126
+ try {
127
+ return await operationArg();
128
+ } finally {
129
+ release();
130
+ }
131
+ }
132
+
133
+ private static remainingManagementTimeout(
134
+ deadlineArg: number | undefined,
135
+ ): number | undefined {
136
+ if (deadlineArg === undefined) return undefined;
137
+ const remaining = deadlineArg - Date.now();
138
+ if (remaining <= 0) {
139
+ throw new LocalNoSqlDbStorageRootRelocationError('IO');
140
+ }
141
+ return remaining;
142
+ }
143
+
144
+ /**
145
+ * Relocate one stopped current native file root, including authenticated
146
+ * roots, under continuous ownership. Preserve the returned source receipt;
147
+ * interrupted operations require the identical request to finish recovery.
148
+ */
149
+ public static async relocateStoppedStorageRoot(
150
+ inputArg: ILocalNoSqlDbStoppedStorageRootRelocationInput,
151
+ optionsArg?: INoSqlDbManagementOperationOptions,
152
+ ): Promise<ILocalNoSqlDbStoppedStorageRootRelocationReceipt> {
153
+ let input: ILocalNoSqlDbStoppedStorageRootRelocationInput;
154
+ try {
155
+ input = normalizeLocalNoSqlDbStoppedStorageRootRelocationInput(inputArg);
156
+ validateNoSqlDbManagementOperationOptions(optionsArg);
157
+ } catch (error) {
158
+ if (error instanceof LocalNoSqlDbStorageRootRelocationError) throw error;
159
+ throw new LocalNoSqlDbStorageRootRelocationError('INVALID_REQUEST');
160
+ }
161
+ if (optionsArg?.signal?.aborted) {
162
+ throw new LocalNoSqlDbStorageRootRelocationError('IO');
163
+ }
164
+ if (process.platform !== 'linux' && process.platform !== 'darwin') {
165
+ throw new LocalNoSqlDbStorageRootRelocationError('UNSUPPORTED');
166
+ }
167
+ const deadline = optionsArg?.timeoutMs === undefined
168
+ ? undefined
169
+ : Date.now() + optionsArg.timeoutMs;
170
+
171
+ return LocalNoSqlDb.withStorageRootRelocationGate(async () => {
172
+ const retainedBridge = LocalNoSqlDb.storageRootRelocationBridge;
173
+ if (retainedBridge) {
174
+ try {
175
+ await retainedBridge.terminate({ gracePeriodMs: 0 });
176
+ } catch {
177
+ throw new LocalNoSqlDbStorageRootRelocationError('RECOVERY_REQUIRED');
178
+ }
179
+ if (!retainedBridge.ownershipReleased) {
180
+ throw new LocalNoSqlDbStorageRootRelocationError('RECOVERY_REQUIRED');
181
+ }
182
+ if (LocalNoSqlDb.storageRootRelocationBridge === retainedBridge) {
183
+ LocalNoSqlDb.storageRootRelocationBridge = undefined;
184
+ }
185
+ }
186
+
187
+ let bridge: RustDbBridge | undefined;
188
+ let receipt: ILocalNoSqlDbStoppedStorageRootRelocationReceipt | undefined;
189
+ let operationError: LocalNoSqlDbStorageRootRelocationError | undefined;
190
+ try {
191
+ bridge = LocalNoSqlDb.createStorageRootRelocationBridge();
192
+ LocalNoSqlDb.storageRootRelocationBridge = bridge;
193
+ const spawnTimeout = LocalNoSqlDb.remainingManagementTimeout(deadline);
194
+ const spawned = await bridge.spawn({
195
+ ...(optionsArg?.signal ? { signal: optionsArg.signal } : {}),
196
+ ...(spawnTimeout === undefined ? {} : { timeoutMs: spawnTimeout }),
197
+ });
198
+ if (!spawned) {
199
+ throw new LocalNoSqlDbStorageRootRelocationError('IO');
200
+ }
201
+ const commandTimeout = LocalNoSqlDb.remainingManagementTimeout(deadline);
202
+ const result = await bridge[rustDbBridgeStorageRootRelocation](input, {
203
+ ...(optionsArg?.signal ? { signal: optionsArg.signal } : {}),
204
+ ...(commandTimeout === undefined ? {} : { timeoutMs: commandTimeout }),
205
+ });
206
+ receipt = normalizeLocalNoSqlDbStoppedStorageRootRelocationReceipt(result, input);
207
+ } catch (error) {
208
+ if (error instanceof LocalNoSqlDbStorageRootRelocationError) {
209
+ operationError = error;
210
+ } else {
211
+ const requestWasDispatched = error instanceof Error
212
+ && 'requestId' in error
213
+ && typeof error.requestId === 'string';
214
+ if (
215
+ requestWasDispatched
216
+ && error instanceof Error
217
+ && 'responseErrorCode' in error
218
+ && isLocalNoSqlDbStorageRootRelocationErrorCode(error.responseErrorCode)
219
+ ) {
220
+ operationError = new LocalNoSqlDbStorageRootRelocationError(error.responseErrorCode);
221
+ } else {
222
+ operationError = new LocalNoSqlDbStorageRootRelocationError(
223
+ requestWasDispatched ? 'RECOVERY_REQUIRED' : 'IO',
224
+ );
225
+ }
226
+ }
227
+ }
228
+
229
+ if (bridge) {
230
+ try {
231
+ await bridge.terminate({ gracePeriodMs: 0 });
232
+ if (!bridge.ownershipReleased) {
233
+ throw new Error('termination was not confirmed');
234
+ }
235
+ if (LocalNoSqlDb.storageRootRelocationBridge === bridge) {
236
+ LocalNoSqlDb.storageRootRelocationBridge = undefined;
237
+ }
238
+ } catch {
239
+ operationError = new LocalNoSqlDbStorageRootRelocationError('RECOVERY_REQUIRED');
240
+ }
241
+ }
242
+ if (operationError) throw operationError;
243
+ if (!receipt) {
244
+ throw new LocalNoSqlDbStorageRootRelocationError('RECOVERY_REQUIRED');
245
+ }
246
+ return receipt;
247
+ });
248
+ }
249
+
250
+ private static async runOfflineInspection<
251
+ TInput extends { folderPath: string },
252
+ TResult,
253
+ >(
254
+ inputArg: TInput,
255
+ optionsArg: INoSqlDbManagementOperationOptions | undefined,
256
+ normalizeInputArg: (inputArg: TInput) => TInput,
257
+ inspectArg: (
258
+ bridgeArg: RustDbBridge,
259
+ inputArg: TInput,
260
+ optionsArg?: INoSqlDbManagementOperationOptions,
261
+ ) => Promise<TResult>,
262
+ ): Promise<TResult> {
263
+ const validatedInput = normalizeInputArg(inputArg);
264
+ validateNoSqlDbManagementOperationOptions(optionsArg);
265
+ optionsArg?.signal?.throwIfAborted();
266
+
267
+ const input = normalizeInputArg({
268
+ ...validatedInput,
269
+ folderPath: path.resolve(validatedInput.folderPath),
270
+ });
271
+ const deadline = optionsArg?.timeoutMs === undefined
272
+ ? undefined
273
+ : Date.now() + optionsArg.timeoutMs;
274
+ const bridge = LocalNoSqlDb.createOfflineInspectionBridge();
275
+ let operationFailed = false;
276
+ let operationError: unknown;
277
+ let result: TResult | undefined;
278
+ try {
279
+ let spawnOptions = optionsArg;
280
+ if (deadline !== undefined) {
281
+ const remainingMs = deadline - Date.now();
282
+ if (remainingMs <= 0) {
283
+ throw new Error('NoSQLDB offline inspection timed out before sidecar spawn');
284
+ }
285
+ spawnOptions = {
286
+ signal: optionsArg?.signal,
287
+ timeoutMs: remainingMs,
288
+ };
289
+ }
290
+ if (!await bridge.spawn(spawnOptions)) {
291
+ throw new Error(
292
+ 'nosqldb Rust binary not found. Set NOSQLDB_RUST_BINARY env var, ' +
293
+ 'install the platform package, or build locally with `tsrust`.',
294
+ );
295
+ }
296
+ let commandOptions = optionsArg;
297
+ if (deadline !== undefined) {
298
+ const remainingMs = deadline - Date.now();
299
+ if (remainingMs <= 0) {
300
+ throw new Error('NoSQLDB offline inspection timed out while starting the sidecar');
301
+ }
302
+ commandOptions = {
303
+ signal: optionsArg?.signal,
304
+ timeoutMs: remainingMs,
305
+ };
306
+ }
307
+ result = await inspectArg(bridge, input, commandOptions);
308
+ } catch (error) {
309
+ operationFailed = true;
310
+ operationError = error;
311
+ }
312
+
313
+ try {
314
+ await bridge.terminate({ gracePeriodMs: 0 });
315
+ } catch (terminationError) {
316
+ if (operationFailed) {
317
+ throw new AggregateError(
318
+ [operationError, terminationError],
319
+ 'NoSQLDB offline inspection failed and sidecar termination was incomplete',
320
+ { cause: operationError },
321
+ );
322
+ }
323
+ throw terminationError;
324
+ }
325
+ if (operationFailed) {
326
+ throw operationError;
327
+ }
328
+ return result as TResult;
329
+ }
330
+
331
+ /**
332
+ * Inspect physical database and collection names while the owning engine is stopped.
333
+ * This starts only the Rust management sidecar and never initializes storage.
334
+ */
335
+ public static async inspectOfflinePhysicalNamespaces(
336
+ inputArg: ILocalNoSqlDbOfflinePhysicalNamespaceInspection,
337
+ optionsArg?: INoSqlDbManagementOperationOptions,
338
+ ): Promise<ILocalNoSqlDbOfflinePhysicalNamespaceInspectionResult> {
339
+ return LocalNoSqlDb.runOfflineInspection(
340
+ inputArg,
341
+ optionsArg,
342
+ normalizeLocalNoSqlDbOfflinePhysicalNamespaceInspectionInput,
343
+ (bridgeArg, input, commandOptions) =>
344
+ bridgeArg.inspectOfflinePhysicalNamespaces(input, commandOptions),
345
+ );
346
+ }
347
+
348
+ /**
349
+ * Inspect one exact string value while the owning LocalNoSqlDb engine is stopped.
350
+ * This starts only the Rust management sidecar and never initializes storage.
351
+ */
352
+ public static async inspectOfflineStringValue(
353
+ inputArg: ILocalNoSqlDbOfflineStringValueInspection,
354
+ optionsArg?: INoSqlDbManagementOperationOptions,
355
+ ): Promise<TLocalNoSqlDbOfflineStringValueInspectionResult> {
356
+ return LocalNoSqlDb.runOfflineInspection(
357
+ inputArg,
358
+ optionsArg,
359
+ normalizeLocalNoSqlDbOfflineInspectionInput,
360
+ (bridgeArg, input, commandOptions) =>
361
+ bridgeArg.inspectOfflineStringValue(input, commandOptions),
362
+ );
363
+ }
364
+
365
+ /**
366
+ * Generate a unique socket path in /tmp
367
+ */
368
+ private generateSocketPath(): string {
369
+ const randomId = crypto.randomBytes(8).toString('hex');
370
+ return path.join(os.tmpdir(), `nosqldb-${randomId}.sock`);
371
+ }
372
+
373
+ /**
374
+ * Check if a Unix socket is alive by attempting to connect.
375
+ */
376
+ private static isSocketAlive(socketPath: string): Promise<boolean> {
377
+ return new Promise((resolve) => {
378
+ const client = net.createConnection({ path: socketPath }, () => {
379
+ client.destroy();
380
+ resolve(true);
381
+ });
382
+ client.on('error', () => {
383
+ resolve(false);
384
+ });
385
+ client.setTimeout(500, () => {
386
+ client.destroy();
387
+ resolve(false);
388
+ });
389
+ });
390
+ }
391
+
392
+ /**
393
+ * Remove stale nosqldb-*.sock files from /tmp.
394
+ * A socket is considered stale if connecting to it fails.
395
+ */
396
+ private static async cleanStaleSockets(): Promise<void> {
397
+ const tmpDir = os.tmpdir();
398
+ let entries: string[];
399
+ try {
400
+ entries = await fs.readdir(tmpDir);
401
+ } catch {
402
+ return;
403
+ }
404
+ const socketFiles = entries.filter(
405
+ (f) => f.startsWith('nosqldb-') && f.endsWith('.sock')
406
+ );
407
+ for (const name of socketFiles) {
408
+ const fullPath = path.join(tmpDir, name);
409
+ try {
410
+ const stat = await fs.stat(fullPath);
411
+ if (!stat.isSocket()) continue;
412
+ const alive = await LocalNoSqlDb.isSocketAlive(fullPath);
413
+ if (!alive) {
414
+ await fs.unlink(fullPath);
415
+ }
416
+ } catch {
417
+ // File may have been removed already; ignore
418
+ }
419
+ }
420
+ }
421
+
422
+ /**
423
+ * Start the local NoSQLDB server and return connection info
424
+ */
425
+ async start(): Promise<ILocalNoSqlDbConnectionInfo> {
426
+ if (this.server) {
427
+ throw new Error('LocalNoSqlDb is already running');
428
+ }
429
+
430
+ // Use provided socket path or generate one
431
+ this.generatedSocketPath = this.options.socketPath ?? this.generateSocketPath();
432
+
433
+ this.server = new NoSqlDbServer({
434
+ binaryPath: this.options.binaryPath,
435
+ storageRehearsalId: this.options.storageRehearsalId,
436
+ auth: this.options.auth,
437
+ fileStorageStartup: this.options.fileStorageStartup,
438
+ socketPath: this.generatedSocketPath,
439
+ storage: 'file',
440
+ storagePath: this.options.folderPath,
441
+ });
442
+ await this.server.start();
443
+
444
+ return {
445
+ socketPath: this.generatedSocketPath,
446
+ connectionUri: this.server.getConnectionUri(),
447
+ };
448
+ }
449
+
450
+ /**
451
+ * Get connection info (throws if not started)
452
+ */
453
+ getConnectionInfo(): ILocalNoSqlDbConnectionInfo {
454
+ if (!this.server || !this.generatedSocketPath) {
455
+ throw new Error('LocalNoSqlDb is not running. Call start() first.');
456
+ }
457
+ return {
458
+ socketPath: this.generatedSocketPath,
459
+ connectionUri: this.server.getConnectionUri(),
460
+ };
461
+ }
462
+
463
+ /**
464
+ * Get the underlying NoSqlDbServer instance (throws if not started)
465
+ */
466
+ getServer(): NoSqlDbServer {
467
+ if (!this.server) {
468
+ throw new Error('LocalNoSqlDb is not running. Call start() first.');
469
+ }
470
+ return this.server;
471
+ }
472
+
473
+ /**
474
+ * Get the connection URI
475
+ */
476
+ getConnectionUri(): string {
477
+ if (!this.server) {
478
+ throw new Error('LocalNoSqlDb is not running. Call start() first.');
479
+ }
480
+ return this.server.getConnectionUri();
481
+ }
482
+
483
+ /**
484
+ * Check if the server is running
485
+ */
486
+ get running(): boolean {
487
+ return this.server !== null && this.server.running;
488
+ }
489
+
490
+ /**
491
+ * Stop the local NoSQLDB server
492
+ */
493
+ async stop(): Promise<void> {
494
+ if (this.server) {
495
+ await this.server.stop();
496
+ this.server = null;
497
+ this.generatedSocketPath = null;
498
+ }
499
+ }
500
+ }
@@ -0,0 +1,19 @@
1
+ export { LocalNoSqlDb } from './classes.localnosqldb.js';
2
+ export type { ILocalNoSqlDbOptions, ILocalNoSqlDbConnectionInfo } from './classes.localnosqldb.js';
3
+ export type {
4
+ ILocalNoSqlDbOfflineInspectionLimits,
5
+ ILocalNoSqlDbOfflinePhysicalNamespaceInspectionLimits,
6
+ ILocalNoSqlDbOfflinePhysicalNamespaceInspection,
7
+ ILocalNoSqlDbOfflinePhysicalNamespaceInspectionResult,
8
+ ILocalNoSqlDbOfflineStringValueInspection,
9
+ TLocalNoSqlDbOfflineStringValueInspectionResult,
10
+ } from '../ts_nosqldb/service-types.js';
11
+ export type {
12
+ ILocalNoSqlDbStoppedStorageRootRelocationInput,
13
+ ILocalNoSqlDbStoppedStorageRootRelocationReceipt,
14
+ TLocalNoSqlDbStorageRootRelocationErrorCode,
15
+ } from '../ts_nosqldb/storage-root-relocation.js';
16
+ export {
17
+ LocalNoSqlDbStorageRootRelocationError,
18
+ localNoSqlDbStorageRootRelocationErrorCodes,
19
+ } from '../ts_nosqldb/storage-root-relocation.js';
@@ -0,0 +1 @@
1
+ // Local module plugins - currently no external dependencies needed