@lossless.org/nosqldb 10.11.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.
Files changed (55) hide show
  1. package/.smartconfig.json +117 -0
  2. package/dist_rust/rustdb_linux_amd64 +0 -0
  3. package/dist_rust/rustdb_linux_amd64.tsrust-build.json +5 -5
  4. package/dist_rust/rustdb_linux_amd64_musl +0 -0
  5. package/dist_rust/rustdb_linux_amd64_musl.tsrust-build.json +5 -5
  6. package/dist_rust/rustdb_linux_arm64 +0 -0
  7. package/dist_rust/rustdb_linux_arm64.tsrust-build.json +5 -5
  8. package/dist_rust/rustdb_linux_arm64_musl +0 -0
  9. package/dist_rust/rustdb_linux_arm64_musl.tsrust-build.json +5 -5
  10. package/dist_rust/rustdb_macos_amd64 +0 -0
  11. package/dist_rust/rustdb_macos_amd64.tsrust-build.json +5 -5
  12. package/dist_rust/rustdb_macos_arm64 +0 -0
  13. package/dist_rust/rustdb_macos_arm64.tsrust-build.json +5 -5
  14. package/dist_ts/00_commitinfo_data.js +2 -2
  15. package/dist_ts/ts_local/classes.localnosqldb.d.ts +15 -4
  16. package/dist_ts/ts_local/classes.localnosqldb.js +31 -11
  17. package/dist_ts/ts_nosqldb/index.d.ts +3 -3
  18. package/dist_ts/ts_nosqldb/index.js +5 -4
  19. package/dist_ts/ts_nosqldb/rust-db-bridge.d.ts +125 -2
  20. package/dist_ts/ts_nosqldb/rust-db-bridge.js +261 -46
  21. package/dist_ts/ts_nosqldb/server/classes.nosqldbserver.d.ts +69 -3
  22. package/dist_ts/ts_nosqldb/server/classes.nosqldbserver.js +91 -12
  23. package/dist_ts/ts_nosqldb/server/index.d.ts +2 -2
  24. package/dist_ts/ts_nosqldb/server/index.js +2 -2
  25. package/dist_ts/ts_nosqldb/stopped-storage.d.ts +9 -1
  26. package/dist_ts/ts_nosqldb/stopped-storage.js +1 -1
  27. package/dist_ts/ts_nosqldb/storage-rehearsal.js +1 -1
  28. package/dist_ts/ts_nosqldb/storage-root-alignment.js +7 -3
  29. package/dist_ts/ts_nosqldb/storage-root-relocation.js +2 -2
  30. package/dist_ts_debugserver/bundled.js +6 -2
  31. package/package.json +28 -15
  32. package/readme.md +470 -730
  33. package/third-party-notices.md +40 -8
  34. package/third_party_licenses/inventory.json +6 -6
  35. package/ts/00_commitinfo_data.ts +8 -0
  36. package/ts/index.ts +85 -0
  37. package/ts/ts_local/classes.localnosqldb.ts +631 -0
  38. package/ts/ts_local/index.ts +19 -0
  39. package/ts/ts_local/plugins.ts +1 -0
  40. package/ts/ts_nosqldb/classes.storagerehearsal.ts +109 -0
  41. package/ts/ts_nosqldb/committed-writes.ts +38 -0
  42. package/ts/ts_nosqldb/file-storage-options.ts +33 -0
  43. package/ts/ts_nosqldb/file-storage-startup.ts +10 -0
  44. package/ts/ts_nosqldb/index.ts +132 -0
  45. package/ts/ts_nosqldb/offline-inspection.ts +375 -0
  46. package/ts/ts_nosqldb/plugins.ts +12 -0
  47. package/ts/ts_nosqldb/resource-fencing.ts +368 -0
  48. package/ts/ts_nosqldb/rust-db-bridge.ts +1309 -0
  49. package/ts/ts_nosqldb/server/classes.nosqldbserver.ts +874 -0
  50. package/ts/ts_nosqldb/server/index.ts +4 -0
  51. package/ts/ts_nosqldb/service-types.ts +499 -0
  52. package/ts/ts_nosqldb/stopped-storage.ts +116 -0
  53. package/ts/ts_nosqldb/storage-rehearsal.ts +215 -0
  54. package/ts/ts_nosqldb/storage-root-alignment.ts +180 -0
  55. package/ts/ts_nosqldb/storage-root-relocation.ts +279 -0
@@ -0,0 +1,4 @@
1
+ // Server module exports
2
+ export { NoSqlDbServer, noSqlDbServerStopGraceMs } from './classes.nosqldbserver.js';
3
+ export type { INoSqlDbServerOptions, INoSqlDbServerStopResult } from './classes.nosqldbserver.js';
4
+ export type { INoSqlDbQueryOptions, INoSqlDbInboundOptions } from '../rust-db-bridge.js';
@@ -0,0 +1,499 @@
1
+ export interface INoSqlDbHealth {
2
+ /** Present on engines supporting stopped root alignment. */
3
+ providerRootId?: string;
4
+ lsn?: string | null;
5
+ running: boolean;
6
+ storage?: 'memory' | 'file';
7
+ fileStorageEngine?: 'mvcc';
8
+ storagePurpose?: 'production' | 'rehearsal';
9
+ storageRehearsalId?: string | null;
10
+ storagePath?: string;
11
+ resourceFencingVersion?: 1;
12
+ resourceFencingSupported?: boolean;
13
+ resourceFencingRequiresDrain?: true;
14
+ allocationFencingVersion?: 1;
15
+ allocationFencingSupported?: boolean;
16
+ allocationFencingRequiresDrain?: true;
17
+ databaseAllocationReadAccessGrantVersion?: 1;
18
+ databaseAllocationReadAccessGrantSupported?: true;
19
+ publicationHoldVersion: 1;
20
+ publicationHoldSupported: boolean;
21
+ publicationHoldRequiresExternalDrain: boolean;
22
+ authEnabled?: boolean;
23
+ authUsers?: number;
24
+ usersPathConfigured?: boolean;
25
+ databaseCount: number;
26
+ collectionCount: number;
27
+ uptimeSeconds?: number;
28
+ }
29
+
30
+ /** Per-operation admission bounds for read-only stopped native storage inspection. */
31
+ export interface ILocalNoSqlDbOfflineInspectionLimits {
32
+ maximumEntries: number;
33
+ maximumTotalFileBytes: number;
34
+ maximumRecords: number;
35
+ maximumRecordBytes: number;
36
+ maximumResultBytes: number;
37
+ }
38
+
39
+ export interface ILocalNoSqlDbOfflineStringValueInspection {
40
+ folderPath: string;
41
+ databaseName: string;
42
+ collectionName: string;
43
+ match: {
44
+ field: string;
45
+ value: string;
46
+ };
47
+ valueField: string;
48
+ limits: ILocalNoSqlDbOfflineInspectionLimits;
49
+ }
50
+
51
+ export type TLocalNoSqlDbOfflineStringValueInspectionResult =
52
+ | { status: 'not-found' }
53
+ | { status: 'found'; value: string };
54
+
55
+ export interface ILocalNoSqlDbOfflinePhysicalNamespaceInspectionLimits {
56
+ maximumEntries: number;
57
+ maximumDatabases: number;
58
+ maximumCollections: number;
59
+ maximumIndexes: number;
60
+ maximumTotalFileBytes: number;
61
+ maximumRecordsPerCollection: number;
62
+ maximumRecordBytes: number;
63
+ maximumResultBytes: number;
64
+ }
65
+
66
+ export interface ILocalNoSqlDbOfflinePhysicalNamespaceInspection {
67
+ folderPath: string;
68
+ limits: ILocalNoSqlDbOfflinePhysicalNamespaceInspectionLimits;
69
+ }
70
+
71
+ export interface ILocalNoSqlDbOfflinePhysicalNamespaceInspectionResult {
72
+ schemaVersion: 1;
73
+ databases: Array<{
74
+ name: string;
75
+ collections: string[];
76
+ }>;
77
+ }
78
+
79
+ export const noSqlDbResourceFenceErrorCodes = [
80
+ 'EFENCE_REQUIRED',
81
+ 'EFENCE_STALE',
82
+ 'EFENCE_SCOPE_MISMATCH',
83
+ 'EFENCE_UNSUPPORTED',
84
+ 'EFENCE_MUTATION_MISMATCH',
85
+ 'EFENCE_OWNERSHIP_MISMATCH',
86
+ 'EFENCE_STATE_CORRUPT',
87
+ 'EFENCE_RECEIPT_LIMIT',
88
+ 'EFENCE_BUSY',
89
+ 'EFENCE_PUBLICATION_HELD',
90
+ 'EFENCE_CAPABILITY_MISMATCH',
91
+ 'EFENCE_NOT_HELD',
92
+ 'EALLOCATION_EXPECTED_ABSENT_CONFLICT',
93
+ 'EALLOCATION_REQUIRED',
94
+ 'EALLOCATION_MISMATCH',
95
+ 'EALLOCATION_GENERATION_EXHAUSTED',
96
+ 'EALLOCATION_DEPROVISIONED',
97
+ ] as const;
98
+
99
+ export type TNoSqlDbResourceFenceErrorCode =
100
+ (typeof noSqlDbResourceFenceErrorCodes)[number];
101
+
102
+ export interface INoSqlDbResourceFence {
103
+ version: 1;
104
+ scopeId: string;
105
+ token: number;
106
+ mutationId: string;
107
+ payloadSha256: string;
108
+ }
109
+
110
+ export interface INoSqlDbResourceFenceReceipt
111
+ extends INoSqlDbResourceFence {
112
+ kind:
113
+ | 'smartdb.database.replace.v1'
114
+ | 'smartdb.database.delete.v1'
115
+ | 'smartdb.database.ensure.v1'
116
+ | 'smartdb.database.allocate.v1';
117
+ /** Digest of the canonical payload interpretation derived by NoSQLDB. */
118
+ effectivePayloadSha256: string;
119
+ provider?: 'smartdb';
120
+ resourceIdSha256?: string;
121
+ holdPublication?: true;
122
+ publicationState?: 'held' | 'released';
123
+ bindingSha256?: string;
124
+ resultSha256?: string;
125
+ receiptSha256?: string;
126
+ publicationCapabilitySha256?: string;
127
+ /** Present only while publication is held; never returned after release. */
128
+ publicationCapability?: string;
129
+ allocation?: INoSqlDbDatabaseAllocationIdentity;
130
+ }
131
+
132
+ export interface INoSqlDbDatabaseAllocationIdentity {
133
+ version: 1;
134
+ provider: 'smartdb';
135
+ state: 'active';
136
+ databaseName: string;
137
+ username: string;
138
+ allocationId: string;
139
+ generation: number;
140
+ principalId: string;
141
+ resourceIdSha256: string;
142
+ bindingSha256: string;
143
+ receiptSha256: string;
144
+ }
145
+
146
+ export interface INoSqlDbDatabaseAllocationLifecycle {
147
+ version: 1;
148
+ provider: 'smartdb';
149
+ state: 'allocating' | 'active' | 'deprovisioning' | 'deprovisioned';
150
+ databaseName: string;
151
+ username: string;
152
+ allocationId: string;
153
+ generation: number;
154
+ principalId: string;
155
+ resourceIdSha256: string;
156
+ bindingSha256: string;
157
+ receiptSha256: string;
158
+ }
159
+
160
+ export interface INoSqlDbDatabaseAllocationReadAccessGrantIdentity {
161
+ version: 1;
162
+ grantId: string;
163
+ principalId: string;
164
+ generation: number;
165
+ }
166
+
167
+ export interface INoSqlDbIssueDatabaseAllocationReadAccessGrantInput {
168
+ databaseName: string;
169
+ grantId: string;
170
+ username: string;
171
+ password: string;
172
+ allocation: INoSqlDbDatabaseAllocationIdentity;
173
+ }
174
+
175
+ export interface INoSqlDbIssueDatabaseAllocationReadAccessGrantResult {
176
+ databaseName: string;
177
+ username: string;
178
+ roles: ['read'];
179
+ authSource: string;
180
+ mongodbUri?: string;
181
+ allocation: INoSqlDbDatabaseAllocationIdentity;
182
+ grant: INoSqlDbDatabaseAllocationReadAccessGrantIdentity;
183
+ issuedAt: number;
184
+ expiresAt: number;
185
+ }
186
+
187
+ export interface INoSqlDbRevokeDatabaseAllocationReadAccessGrantInput {
188
+ databaseName: string;
189
+ grantId: string;
190
+ allocation: INoSqlDbDatabaseAllocationIdentity;
191
+ }
192
+
193
+ export interface INoSqlDbRevokeDatabaseAllocationReadAccessGrantResult {
194
+ databaseName: string;
195
+ grantId: string;
196
+ allocation: INoSqlDbDatabaseAllocationIdentity;
197
+ revoked: true;
198
+ }
199
+
200
+ export const noSqlDbAllocationReadAccessGrantErrorCodes = [
201
+ 'EGRANT_INVALID_REQUEST',
202
+ 'EGRANT_CREDENTIAL_MISMATCH',
203
+ 'EGRANT_CONFLICT',
204
+ 'EGRANT_REPLAY_CAPACITY',
205
+ 'EGRANT_STATE_UNAVAILABLE',
206
+ ] as const;
207
+
208
+ export type TNoSqlDbAllocationReadAccessGrantErrorCode =
209
+ (typeof noSqlDbAllocationReadAccessGrantErrorCodes)[number];
210
+
211
+ export class NoSqlDbAllocationReadAccessGrantError extends Error {
212
+ public readonly code: TNoSqlDbAllocationReadAccessGrantErrorCode;
213
+
214
+ public constructor(
215
+ codeArg: TNoSqlDbAllocationReadAccessGrantErrorCode,
216
+ messageArg: string,
217
+ causeArg?: unknown,
218
+ ) {
219
+ super(`${codeArg}: ${messageArg}`, { cause: causeArg });
220
+ this.name = 'NoSqlDbAllocationReadAccessGrantError';
221
+ this.code = codeArg;
222
+ }
223
+ }
224
+
225
+ export const normalizeNoSqlDbAllocationReadAccessGrantError = (
226
+ errorArg: unknown,
227
+ codeArg?: unknown,
228
+ ): Error => {
229
+ const error =
230
+ errorArg instanceof Error
231
+ ? errorArg
232
+ : new Error(String(errorArg));
233
+ if (
234
+ typeof codeArg !== 'string'
235
+ || !noSqlDbAllocationReadAccessGrantErrorCodes.includes(
236
+ codeArg as TNoSqlDbAllocationReadAccessGrantErrorCode,
237
+ )
238
+ ) {
239
+ return error;
240
+ }
241
+ return new NoSqlDbAllocationReadAccessGrantError(
242
+ codeArg as TNoSqlDbAllocationReadAccessGrantErrorCode,
243
+ error.message,
244
+ error,
245
+ );
246
+ };
247
+
248
+ export interface INoSqlDbHeldPublicationReceipt
249
+ extends INoSqlDbResourceFenceReceipt {
250
+ provider: 'smartdb';
251
+ resourceIdSha256: string;
252
+ holdPublication: true;
253
+ publicationState: 'held';
254
+ bindingSha256: string;
255
+ resultSha256: string;
256
+ receiptSha256: string;
257
+ publicationCapabilitySha256: string;
258
+ publicationCapability: string;
259
+ }
260
+
261
+ export interface INoSqlDbCommitDatabasePublicationInput {
262
+ databaseName: string;
263
+ resourceFence: INoSqlDbHeldPublicationReceipt;
264
+ }
265
+
266
+ /**
267
+ * Cancellation and deadline ownership for one durable database-management
268
+ * operation. Aborting or timing out is fail-stop: NoSQLDB terminates the Rust
269
+ * engine so the operation cannot continue after the caller has released it.
270
+ */
271
+ export interface INoSqlDbManagementOperationOptions {
272
+ signal?: AbortSignal;
273
+ timeoutMs?: number;
274
+ }
275
+
276
+ export interface INoSqlDbDatabaseResourceFenceState {
277
+ providerRootId?: string;
278
+ version: 1;
279
+ databaseName: string;
280
+ resourceIdSha256: string;
281
+ scopeId: string;
282
+ highestToken: number;
283
+ publicationState: 'requested' | 'publishing' | 'held' | 'released';
284
+ allocation?: INoSqlDbDatabaseAllocationLifecycle;
285
+ }
286
+
287
+ export class NoSqlDbResourceFenceError extends Error {
288
+ public readonly code: TNoSqlDbResourceFenceErrorCode;
289
+
290
+ public constructor(
291
+ codeArg: TNoSqlDbResourceFenceErrorCode,
292
+ messageArg: string,
293
+ causeArg?: unknown,
294
+ ) {
295
+ super(`${codeArg}: ${messageArg}`, { cause: causeArg });
296
+ this.name = 'NoSqlDbResourceFenceError';
297
+ this.code = codeArg;
298
+ }
299
+ }
300
+
301
+ export const normalizeNoSqlDbResourceFenceError = (
302
+ errorArg: unknown,
303
+ ): Error => {
304
+ const error =
305
+ errorArg instanceof Error
306
+ ? errorArg
307
+ : new Error(String(errorArg));
308
+ const match = /^((?:EFENCE|EALLOCATION)_[A-Z_]+):\s*(.*)$/s.exec(error.message);
309
+ if (
310
+ !match ||
311
+ !noSqlDbResourceFenceErrorCodes.includes(
312
+ match[1] as TNoSqlDbResourceFenceErrorCode,
313
+ )
314
+ ) {
315
+ return error;
316
+ }
317
+ return new NoSqlDbResourceFenceError(
318
+ match[1] as TNoSqlDbResourceFenceErrorCode,
319
+ match[2],
320
+ error,
321
+ );
322
+ };
323
+
324
+ export interface INoSqlDbDatabaseTenantInput {
325
+ databaseName: string;
326
+ username: string;
327
+ password: string;
328
+ roles?: string[];
329
+ }
330
+
331
+ export interface INoSqlDbEnsureDatabaseTenantInput
332
+ extends INoSqlDbDatabaseTenantInput {
333
+ fence: INoSqlDbResourceFence;
334
+ allocation?: INoSqlDbDatabaseAllocationIdentity;
335
+ }
336
+
337
+ export interface INoSqlDbAttestDatabaseTenantInput {
338
+ databaseName: string;
339
+ username: string;
340
+ roles: string[];
341
+ password: string;
342
+ }
343
+
344
+ export interface INoSqlDbAttestDatabaseTenantResult {
345
+ matches: boolean;
346
+ }
347
+
348
+ export interface INoSqlDbAllocateDatabaseTenantInput
349
+ extends INoSqlDbDatabaseTenantInput {
350
+ expectedAbsent: true;
351
+ fence: INoSqlDbResourceFence;
352
+ }
353
+
354
+ export interface INoSqlDbDeleteDatabaseTenantInput {
355
+ databaseName: string;
356
+ /**
357
+ * Exact tenant user to revoke. For a fenced deletion this identity is
358
+ * included in the durable mutation and is removed idempotently after the
359
+ * database deletion is durable. Omitting it from a fenced deletion
360
+ * preserves auth users; an unfenced omission retains the legacy behavior
361
+ * of deleting every user for the database. If the database exists, a
362
+ * fenced deletion fails closed unless this exact user currently owns it.
363
+ */
364
+ username?: string;
365
+ fence?: INoSqlDbResourceFence;
366
+ allocation?: INoSqlDbDatabaseAllocationIdentity;
367
+ /** Keep the database data plane closed until commitDatabasePublication. */
368
+ holdPublication?: boolean;
369
+ }
370
+
371
+ export interface INoSqlDbRotateDatabaseTenantPasswordInput {
372
+ username: string;
373
+ password: string;
374
+ }
375
+
376
+ export interface INoSqlDbDatabaseTenantDescriptor {
377
+ databaseName: string;
378
+ username: string;
379
+ roles: string[];
380
+ authSource: string;
381
+ mongodbUri?: string;
382
+ resourceFence?: INoSqlDbResourceFenceReceipt;
383
+ allocation?: INoSqlDbDatabaseAllocationIdentity;
384
+ }
385
+
386
+ export interface INoSqlDbEnsureDatabaseTenantResult
387
+ extends INoSqlDbDatabaseTenantDescriptor {
388
+ resourceFence: INoSqlDbResourceFenceReceipt;
389
+ }
390
+
391
+ export interface INoSqlDbAllocateDatabaseTenantResult
392
+ extends INoSqlDbDatabaseTenantDescriptor {
393
+ resourceFence: INoSqlDbResourceFenceReceipt;
394
+ allocation: INoSqlDbDatabaseAllocationIdentity;
395
+ }
396
+
397
+ export interface INoSqlDbDeleteDatabaseTenantResult {
398
+ databaseName: string;
399
+ /**
400
+ * Users removed during the completing attempt. This can be zero after
401
+ * crash recovery when the exact user was durably removed by the prior
402
+ * attempt.
403
+ */
404
+ deletedUsers: number;
405
+ databaseDropped: boolean;
406
+ /**
407
+ * Present for fenced calls. False means the bound exact username is now
408
+ * absent; true means no username was supplied and auth users were preserved.
409
+ */
410
+ usersPreserved?: boolean;
411
+ resourceFence?: INoSqlDbResourceFenceReceipt;
412
+ allocation?: INoSqlDbDatabaseAllocationIdentity;
413
+ }
414
+
415
+ export interface INoSqlDbDatabaseExportCollection {
416
+ name: string;
417
+ /**
418
+ * Collection options exactly as `listCollections` reports them
419
+ * (`validator`, `validationLevel`, `validationAction`). Present only when
420
+ * the collection has options; engines up to 10.6.1 refuse such an export.
421
+ */
422
+ options?: Record<string, any>;
423
+ documents: Record<string, any>[];
424
+ indexes: Record<string, any>[];
425
+ }
426
+
427
+ export interface INoSqlDbDatabaseExportLimits {
428
+ maxEncodedBytes?: number;
429
+ maxCollections?: number;
430
+ maxDocuments?: number;
431
+ maxIndexes?: number;
432
+ }
433
+
434
+ export interface INoSqlDbExportDatabaseInput {
435
+ databaseName: string;
436
+ limits?: INoSqlDbDatabaseExportLimits;
437
+ }
438
+
439
+ /** Management-only read under an exact, still-held publication capability. */
440
+ export interface INoSqlDbExportHeldDatabaseInput extends INoSqlDbExportDatabaseInput {
441
+ resourceFence: INoSqlDbHeldPublicationReceipt;
442
+ }
443
+
444
+ export interface INoSqlDbDatabaseContentDigestLimits {
445
+ maxScannedBsonBytes?: number;
446
+ maxCollections?: number;
447
+ maxDocuments?: number;
448
+ maxIndexes?: number;
449
+ }
450
+
451
+ export interface INoSqlDbGetDatabaseContentDigestInput {
452
+ databaseName: string;
453
+ limits?: INoSqlDbDatabaseContentDigestLimits;
454
+ }
455
+
456
+ export interface INoSqlDbDatabaseContentDigest {
457
+ format: 'smartdb.database.content-digest.v1';
458
+ algorithm: 'sha256';
459
+ databaseName: string;
460
+ sha256: string;
461
+ scannedBsonBytes: number;
462
+ collections: number;
463
+ documents: number;
464
+ indexes: number;
465
+ }
466
+
467
+ export interface INoSqlDbDatabaseExport {
468
+ format: 'smartdb.database.export.v1';
469
+ databaseName: string;
470
+ exportedAtMs: number;
471
+ collections: INoSqlDbDatabaseExportCollection[];
472
+ }
473
+
474
+ export interface INoSqlDbImportDatabaseInput {
475
+ databaseName: string;
476
+ source: INoSqlDbDatabaseExport;
477
+ /**
478
+ * Exact exclusive tenant owner required for this replacement. When used
479
+ * with a fence, the username is included in the provider-derived mutation
480
+ * identity. Omission preserves the original version 1 replacement profile.
481
+ */
482
+ username?: string;
483
+ fence?: INoSqlDbResourceFence;
484
+ allocation?: INoSqlDbDatabaseAllocationIdentity;
485
+ /** Keep the database data plane closed until commitDatabasePublication. */
486
+ holdPublication?: boolean;
487
+ }
488
+
489
+ export interface INoSqlDbImportDatabaseResult {
490
+ databaseName: string;
491
+ collections: number;
492
+ documents: number;
493
+ resourceFence?: INoSqlDbResourceFenceReceipt;
494
+ allocation?: INoSqlDbDatabaseAllocationIdentity;
495
+ }
496
+
497
+ export type TNoSqlDbCommitDatabasePublicationResult =
498
+ | INoSqlDbImportDatabaseResult
499
+ | INoSqlDbDeleteDatabaseTenantResult;
@@ -0,0 +1,116 @@
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
+ /**
45
+ * The generation inventory of a root that a 10.x release wrote, as this
46
+ * release's audit reports it and a 10.x engine's audit of its own root does.
47
+ * A root of this release keeps none: its report has no `generations` and
48
+ * says `storageFormat: 'format-3'`.
49
+ */
50
+ generations?: { manifest: string; onDisk: string[] };
51
+ /** `'format-3'` for a root of this release; absent for a root that a 10.x release wrote. */
52
+ storageFormat?: 'format-3';
53
+ alignmentEligible: boolean;
54
+ reasons: string[];
55
+ physicalAdmissionViolations: [];
56
+ }
57
+ export interface ILocalNoSqlDbUnavailableStorageRoot {
58
+ status: 'unavailable';
59
+ stage: 'admission' | 'audit';
60
+ alignmentEligible: false;
61
+ reasons: string[];
62
+ /** Bounded advisory evidence. No logical identity or pin is trusted here. */
63
+ physicalAdmissionViolations: INoSqlDbPhysicalAdmissionViolation[];
64
+ }
65
+ export type TLocalNoSqlDbStoppedStorageRootInspection =
66
+ ILocalNoSqlDbAuditedStorageRoot | ILocalNoSqlDbUnavailableStorageRoot;
67
+
68
+ export class NoSqlDbStoppedOperationError extends Error {
69
+ public readonly responseErrorCode: TLocalNoSqlDbStorageRootRelocationErrorCode;
70
+ public constructor(public readonly code: TLocalNoSqlDbStorageRootRelocationErrorCode) {
71
+ super(`NoSQLDB stopped operation failed with code ${code}`);
72
+ this.name = 'NoSqlDbStoppedOperationError';
73
+ this.responseErrorCode = code;
74
+ }
75
+ }
76
+
77
+ export const validateNoSqlDbStoppedOperationOptions = (options?: INoSqlDbStoppedOperationOptions): void => {
78
+ validateNoSqlDbManagementOperationOptions(options);
79
+ if (options?.binaryPath !== undefined && (typeof options.binaryPath !== 'string'
80
+ || !options.binaryPath.length || options.binaryPath.includes('\0'))) {
81
+ throw new NoSqlDbStoppedOperationError('INVALID_REQUEST');
82
+ }
83
+ };
84
+
85
+ export const normalizeStoppedStorageRootInspectionInput = (
86
+ input: ILocalNoSqlDbStoppedStorageRootInspectionInput,
87
+ ): ILocalNoSqlDbStoppedStorageRootInspectionInput => {
88
+ if (!input || Object.getPrototypeOf(input) !== Object.prototype
89
+ || Reflect.ownKeys(input).length !== 1
90
+ || !Object.getOwnPropertyDescriptor(input, 'folderPath')?.enumerable
91
+ || !('value' in Object.getOwnPropertyDescriptor(input, 'folderPath')!)) {
92
+ throw new NoSqlDbStoppedOperationError('INVALID_REQUEST');
93
+ }
94
+ const folderPath = input.folderPath;
95
+ if (typeof folderPath !== 'string' || plugins.buffer.Buffer.byteLength(folderPath) > 4095
96
+ || !plugins.path.isAbsolute(folderPath) || plugins.path.dirname(folderPath) === folderPath
97
+ || folderPath.endsWith('/') || folderPath.includes('//') || /\p{Cc}/u.test(folderPath)
98
+ || folderPath.split('/').some(part => part === '.' || part === '..' || part.startsWith('.smartdb-rehearsal-'))) {
99
+ throw new NoSqlDbStoppedOperationError('INVALID_REQUEST');
100
+ }
101
+ return { folderPath };
102
+ };
103
+
104
+ /** Only a confirmed Rust response for this exact command proves non-dispatch.
105
+ * Transport errors with the same text remain ambiguous. */
106
+ export const isStoppedOperationUnknownMethod = (error: unknown, method: string): boolean =>
107
+ error instanceof Error && 'code' in error
108
+ && error.code === 'ERR_RUST_BRIDGE_REQUEST_RUST_RESPONSE'
109
+ && 'requestId' in error && typeof error.requestId === 'string' && error.requestId.length > 0
110
+ && 'method' in error && error.method === method
111
+ && (('responseErrorCode' in error && error.responseErrorCode === 'UNKNOWN_METHOD')
112
+ || ((!('responseErrorCode' in error) || error.responseErrorCode === undefined)
113
+ && error.message === `Unknown method: ${method}`));
114
+
115
+ export const normalizeStoppedOperationUnknownMethod = (error: unknown, method: string): unknown =>
116
+ isStoppedOperationUnknownMethod(error, method) ? new NoSqlDbStoppedOperationError('UNSUPPORTED') : error;