@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.
- package/.smartconfig.json +107 -0
- package/dist_rust/rustdb_linux_amd64 +0 -0
- package/dist_rust/rustdb_linux_amd64.tsrust-build.json +14 -0
- package/dist_rust/rustdb_linux_arm64 +0 -0
- package/dist_rust/rustdb_linux_arm64.tsrust-build.json +14 -0
- package/dist_rust/rustdb_macos_amd64 +0 -0
- package/dist_rust/rustdb_macos_amd64.tsrust-build.json +14 -0
- package/dist_rust/rustdb_macos_arm64 +0 -0
- package/dist_rust/rustdb_macos_arm64.tsrust-build.json +14 -0
- package/dist_ts/00_commitinfo_data.d.ts +8 -0
- package/dist_ts/00_commitinfo_data.js +9 -0
- package/dist_ts/index.d.ts +8 -0
- package/dist_ts/index.js +10 -0
- package/dist_ts/ts_local/classes.localnosqldb.d.ts +119 -0
- package/dist_ts/ts_local/classes.localnosqldb.js +386 -0
- package/dist_ts/ts_local/index.d.ts +5 -0
- package/dist_ts/ts_local/index.js +3 -0
- package/dist_ts/ts_local/plugins.d.ts +1 -0
- package/dist_ts/ts_local/plugins.js +3 -0
- package/dist_ts/ts_nosqldb/classes.storagerehearsal.d.ts +19 -0
- package/dist_ts/ts_nosqldb/classes.storagerehearsal.js +122 -0
- package/dist_ts/ts_nosqldb/file-storage-options.d.ts +13 -0
- package/dist_ts/ts_nosqldb/file-storage-options.js +24 -0
- package/dist_ts/ts_nosqldb/file-storage-startup.d.ts +3 -0
- package/dist_ts/ts_nosqldb/file-storage-startup.js +6 -0
- package/dist_ts/ts_nosqldb/index.d.ts +14 -0
- package/dist_ts/ts_nosqldb/index.js +11 -0
- package/dist_ts/ts_nosqldb/offline-inspection.d.ts +72 -0
- package/dist_ts/ts_nosqldb/offline-inspection.js +282 -0
- package/dist_ts/ts_nosqldb/plugins.d.ts +7 -0
- package/dist_ts/ts_nosqldb/plugins.js +10 -0
- package/dist_ts/ts_nosqldb/resource-fencing.d.ts +39 -0
- package/dist_ts/ts_nosqldb/resource-fencing.js +271 -0
- package/dist_ts/ts_nosqldb/rust-db-bridge.d.ts +213 -0
- package/dist_ts/ts_nosqldb/rust-db-bridge.js +439 -0
- package/dist_ts/ts_nosqldb/server/classes.nosqldbserver.d.ts +204 -0
- package/dist_ts/ts_nosqldb/server/classes.nosqldbserver.js +456 -0
- package/dist_ts/ts_nosqldb/server/index.d.ts +3 -0
- package/dist_ts/ts_nosqldb/server/index.js +3 -0
- package/dist_ts/ts_nosqldb/service-types.d.ts +341 -0
- package/dist_ts/ts_nosqldb/service-types.js +62 -0
- package/dist_ts/ts_nosqldb/storage-rehearsal.d.ts +103 -0
- package/dist_ts/ts_nosqldb/storage-rehearsal.js +131 -0
- package/dist_ts/ts_nosqldb/storage-root-relocation.d.ts +27 -0
- package/dist_ts/ts_nosqldb/storage-root-relocation.js +211 -0
- package/dist_ts_debugserver/bundled.d.ts +4 -0
- package/dist_ts_debugserver/bundled.js +16 -0
- package/dist_ts_debugserver/classes.debugserver.d.ts +36 -0
- package/dist_ts_debugserver/classes.debugserver.js +95 -0
- package/dist_ts_debugserver/index.d.ts +2 -0
- package/dist_ts_debugserver/index.js +2 -0
- package/dist_ts_debugserver/plugins.d.ts +2 -0
- package/dist_ts_debugserver/plugins.js +3 -0
- package/dist_ts_debugui/index.d.ts +2 -0
- package/dist_ts_debugui/index.js +2 -0
- package/dist_ts_debugui/nosqldb-debugui.d.ts +62 -0
- package/dist_ts_debugui/nosqldb-debugui.js +1132 -0
- package/dist_ts_debugui/plugins.d.ts +1 -0
- package/dist_ts_debugui/plugins.js +2 -0
- package/license +21 -0
- package/package.json +82 -0
- package/readme.md +1733 -0
- package/third-party-notices.md +925 -0
- package/ts/00_commitinfo_data.ts +8 -0
- package/ts/index.ts +85 -0
- package/ts/ts_local/classes.localnosqldb.ts +500 -0
- package/ts/ts_local/index.ts +19 -0
- package/ts/ts_local/plugins.ts +1 -0
- package/ts/ts_nosqldb/classes.storagerehearsal.ts +109 -0
- package/ts/ts_nosqldb/file-storage-options.ts +33 -0
- package/ts/ts_nosqldb/file-storage-startup.ts +9 -0
- package/ts/ts_nosqldb/index.ts +104 -0
- package/ts/ts_nosqldb/offline-inspection.ts +375 -0
- package/ts/ts_nosqldb/plugins.ts +12 -0
- package/ts/ts_nosqldb/resource-fencing.ts +368 -0
- package/ts/ts_nosqldb/rust-db-bridge.ts +964 -0
- package/ts/ts_nosqldb/server/classes.nosqldbserver.ts +696 -0
- package/ts/ts_nosqldb/server/index.ts +4 -0
- package/ts/ts_nosqldb/service-types.ts +484 -0
- package/ts/ts_nosqldb/storage-rehearsal.ts +215 -0
- package/ts/ts_nosqldb/storage-root-relocation.ts +277 -0
|
@@ -0,0 +1,484 @@
|
|
|
1
|
+
export interface INoSqlDbHealth {
|
|
2
|
+
running: boolean;
|
|
3
|
+
storage?: 'memory' | 'file';
|
|
4
|
+
fileStorageEngine?: 'mvcc';
|
|
5
|
+
storagePurpose?: 'production' | 'rehearsal';
|
|
6
|
+
storageRehearsalId?: string | null;
|
|
7
|
+
storagePath?: string;
|
|
8
|
+
resourceFencingVersion?: 1;
|
|
9
|
+
resourceFencingSupported?: boolean;
|
|
10
|
+
resourceFencingRequiresDrain?: true;
|
|
11
|
+
allocationFencingVersion?: 1;
|
|
12
|
+
allocationFencingSupported?: boolean;
|
|
13
|
+
allocationFencingRequiresDrain?: true;
|
|
14
|
+
databaseAllocationReadAccessGrantVersion?: 1;
|
|
15
|
+
databaseAllocationReadAccessGrantSupported?: true;
|
|
16
|
+
publicationHoldVersion: 1;
|
|
17
|
+
publicationHoldSupported: boolean;
|
|
18
|
+
publicationHoldRequiresExternalDrain: boolean;
|
|
19
|
+
authEnabled?: boolean;
|
|
20
|
+
authUsers?: number;
|
|
21
|
+
usersPathConfigured?: boolean;
|
|
22
|
+
databaseCount: number;
|
|
23
|
+
collectionCount: number;
|
|
24
|
+
uptimeSeconds?: number;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Per-operation admission bounds for read-only stopped native storage inspection. */
|
|
28
|
+
export interface ILocalNoSqlDbOfflineInspectionLimits {
|
|
29
|
+
maximumEntries: number;
|
|
30
|
+
maximumTotalFileBytes: number;
|
|
31
|
+
maximumRecords: number;
|
|
32
|
+
maximumRecordBytes: number;
|
|
33
|
+
maximumResultBytes: number;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface ILocalNoSqlDbOfflineStringValueInspection {
|
|
37
|
+
folderPath: string;
|
|
38
|
+
databaseName: string;
|
|
39
|
+
collectionName: string;
|
|
40
|
+
match: {
|
|
41
|
+
field: string;
|
|
42
|
+
value: string;
|
|
43
|
+
};
|
|
44
|
+
valueField: string;
|
|
45
|
+
limits: ILocalNoSqlDbOfflineInspectionLimits;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export type TLocalNoSqlDbOfflineStringValueInspectionResult =
|
|
49
|
+
| { status: 'not-found' }
|
|
50
|
+
| { status: 'found'; value: string };
|
|
51
|
+
|
|
52
|
+
export interface ILocalNoSqlDbOfflinePhysicalNamespaceInspectionLimits {
|
|
53
|
+
maximumEntries: number;
|
|
54
|
+
maximumDatabases: number;
|
|
55
|
+
maximumCollections: number;
|
|
56
|
+
maximumIndexes: number;
|
|
57
|
+
maximumTotalFileBytes: number;
|
|
58
|
+
maximumRecordsPerCollection: number;
|
|
59
|
+
maximumRecordBytes: number;
|
|
60
|
+
maximumResultBytes: number;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export interface ILocalNoSqlDbOfflinePhysicalNamespaceInspection {
|
|
64
|
+
folderPath: string;
|
|
65
|
+
limits: ILocalNoSqlDbOfflinePhysicalNamespaceInspectionLimits;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export interface ILocalNoSqlDbOfflinePhysicalNamespaceInspectionResult {
|
|
69
|
+
schemaVersion: 1;
|
|
70
|
+
databases: Array<{
|
|
71
|
+
name: string;
|
|
72
|
+
collections: string[];
|
|
73
|
+
}>;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export const noSqlDbResourceFenceErrorCodes = [
|
|
77
|
+
'EFENCE_REQUIRED',
|
|
78
|
+
'EFENCE_STALE',
|
|
79
|
+
'EFENCE_SCOPE_MISMATCH',
|
|
80
|
+
'EFENCE_UNSUPPORTED',
|
|
81
|
+
'EFENCE_MUTATION_MISMATCH',
|
|
82
|
+
'EFENCE_OWNERSHIP_MISMATCH',
|
|
83
|
+
'EFENCE_STATE_CORRUPT',
|
|
84
|
+
'EFENCE_RECEIPT_LIMIT',
|
|
85
|
+
'EFENCE_BUSY',
|
|
86
|
+
'EFENCE_PUBLICATION_HELD',
|
|
87
|
+
'EFENCE_CAPABILITY_MISMATCH',
|
|
88
|
+
'EFENCE_NOT_HELD',
|
|
89
|
+
'EALLOCATION_EXPECTED_ABSENT_CONFLICT',
|
|
90
|
+
'EALLOCATION_REQUIRED',
|
|
91
|
+
'EALLOCATION_MISMATCH',
|
|
92
|
+
'EALLOCATION_GENERATION_EXHAUSTED',
|
|
93
|
+
'EALLOCATION_DEPROVISIONED',
|
|
94
|
+
] as const;
|
|
95
|
+
|
|
96
|
+
export type TNoSqlDbResourceFenceErrorCode =
|
|
97
|
+
(typeof noSqlDbResourceFenceErrorCodes)[number];
|
|
98
|
+
|
|
99
|
+
export interface INoSqlDbResourceFence {
|
|
100
|
+
version: 1;
|
|
101
|
+
scopeId: string;
|
|
102
|
+
token: number;
|
|
103
|
+
mutationId: string;
|
|
104
|
+
payloadSha256: string;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export interface INoSqlDbResourceFenceReceipt
|
|
108
|
+
extends INoSqlDbResourceFence {
|
|
109
|
+
kind:
|
|
110
|
+
| 'smartdb.database.replace.v1'
|
|
111
|
+
| 'smartdb.database.delete.v1'
|
|
112
|
+
| 'smartdb.database.ensure.v1'
|
|
113
|
+
| 'smartdb.database.allocate.v1';
|
|
114
|
+
/** Digest of the canonical payload interpretation derived by NoSQLDB. */
|
|
115
|
+
effectivePayloadSha256: string;
|
|
116
|
+
provider?: 'smartdb';
|
|
117
|
+
resourceIdSha256?: string;
|
|
118
|
+
holdPublication?: true;
|
|
119
|
+
publicationState?: 'held' | 'released';
|
|
120
|
+
bindingSha256?: string;
|
|
121
|
+
resultSha256?: string;
|
|
122
|
+
receiptSha256?: string;
|
|
123
|
+
publicationCapabilitySha256?: string;
|
|
124
|
+
/** Present only while publication is held; never returned after release. */
|
|
125
|
+
publicationCapability?: string;
|
|
126
|
+
allocation?: INoSqlDbDatabaseAllocationIdentity;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export interface INoSqlDbDatabaseAllocationIdentity {
|
|
130
|
+
version: 1;
|
|
131
|
+
provider: 'smartdb';
|
|
132
|
+
state: 'active';
|
|
133
|
+
databaseName: string;
|
|
134
|
+
username: string;
|
|
135
|
+
allocationId: string;
|
|
136
|
+
generation: number;
|
|
137
|
+
principalId: string;
|
|
138
|
+
resourceIdSha256: string;
|
|
139
|
+
bindingSha256: string;
|
|
140
|
+
receiptSha256: string;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export interface INoSqlDbDatabaseAllocationLifecycle {
|
|
144
|
+
version: 1;
|
|
145
|
+
provider: 'smartdb';
|
|
146
|
+
state: 'allocating' | 'active' | 'deprovisioning' | 'deprovisioned';
|
|
147
|
+
databaseName: string;
|
|
148
|
+
username: string;
|
|
149
|
+
allocationId: string;
|
|
150
|
+
generation: number;
|
|
151
|
+
principalId: string;
|
|
152
|
+
resourceIdSha256: string;
|
|
153
|
+
bindingSha256: string;
|
|
154
|
+
receiptSha256: string;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export interface INoSqlDbDatabaseAllocationReadAccessGrantIdentity {
|
|
158
|
+
version: 1;
|
|
159
|
+
grantId: string;
|
|
160
|
+
principalId: string;
|
|
161
|
+
generation: number;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export interface INoSqlDbIssueDatabaseAllocationReadAccessGrantInput {
|
|
165
|
+
databaseName: string;
|
|
166
|
+
grantId: string;
|
|
167
|
+
username: string;
|
|
168
|
+
password: string;
|
|
169
|
+
allocation: INoSqlDbDatabaseAllocationIdentity;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export interface INoSqlDbIssueDatabaseAllocationReadAccessGrantResult {
|
|
173
|
+
databaseName: string;
|
|
174
|
+
username: string;
|
|
175
|
+
roles: ['read'];
|
|
176
|
+
authSource: string;
|
|
177
|
+
mongodbUri?: string;
|
|
178
|
+
allocation: INoSqlDbDatabaseAllocationIdentity;
|
|
179
|
+
grant: INoSqlDbDatabaseAllocationReadAccessGrantIdentity;
|
|
180
|
+
issuedAt: number;
|
|
181
|
+
expiresAt: number;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export interface INoSqlDbRevokeDatabaseAllocationReadAccessGrantInput {
|
|
185
|
+
databaseName: string;
|
|
186
|
+
grantId: string;
|
|
187
|
+
allocation: INoSqlDbDatabaseAllocationIdentity;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export interface INoSqlDbRevokeDatabaseAllocationReadAccessGrantResult {
|
|
191
|
+
databaseName: string;
|
|
192
|
+
grantId: string;
|
|
193
|
+
allocation: INoSqlDbDatabaseAllocationIdentity;
|
|
194
|
+
revoked: true;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export const noSqlDbAllocationReadAccessGrantErrorCodes = [
|
|
198
|
+
'EGRANT_INVALID_REQUEST',
|
|
199
|
+
'EGRANT_CREDENTIAL_MISMATCH',
|
|
200
|
+
'EGRANT_CONFLICT',
|
|
201
|
+
'EGRANT_REPLAY_CAPACITY',
|
|
202
|
+
'EGRANT_STATE_UNAVAILABLE',
|
|
203
|
+
] as const;
|
|
204
|
+
|
|
205
|
+
export type TNoSqlDbAllocationReadAccessGrantErrorCode =
|
|
206
|
+
(typeof noSqlDbAllocationReadAccessGrantErrorCodes)[number];
|
|
207
|
+
|
|
208
|
+
export class NoSqlDbAllocationReadAccessGrantError extends Error {
|
|
209
|
+
public readonly code: TNoSqlDbAllocationReadAccessGrantErrorCode;
|
|
210
|
+
|
|
211
|
+
public constructor(
|
|
212
|
+
codeArg: TNoSqlDbAllocationReadAccessGrantErrorCode,
|
|
213
|
+
messageArg: string,
|
|
214
|
+
causeArg?: unknown,
|
|
215
|
+
) {
|
|
216
|
+
super(`${codeArg}: ${messageArg}`, { cause: causeArg });
|
|
217
|
+
this.name = 'NoSqlDbAllocationReadAccessGrantError';
|
|
218
|
+
this.code = codeArg;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
export const normalizeNoSqlDbAllocationReadAccessGrantError = (
|
|
223
|
+
errorArg: unknown,
|
|
224
|
+
codeArg?: unknown,
|
|
225
|
+
): Error => {
|
|
226
|
+
const error =
|
|
227
|
+
errorArg instanceof Error
|
|
228
|
+
? errorArg
|
|
229
|
+
: new Error(String(errorArg));
|
|
230
|
+
if (
|
|
231
|
+
typeof codeArg !== 'string'
|
|
232
|
+
|| !noSqlDbAllocationReadAccessGrantErrorCodes.includes(
|
|
233
|
+
codeArg as TNoSqlDbAllocationReadAccessGrantErrorCode,
|
|
234
|
+
)
|
|
235
|
+
) {
|
|
236
|
+
return error;
|
|
237
|
+
}
|
|
238
|
+
return new NoSqlDbAllocationReadAccessGrantError(
|
|
239
|
+
codeArg as TNoSqlDbAllocationReadAccessGrantErrorCode,
|
|
240
|
+
error.message,
|
|
241
|
+
error,
|
|
242
|
+
);
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
export interface INoSqlDbHeldPublicationReceipt
|
|
246
|
+
extends INoSqlDbResourceFenceReceipt {
|
|
247
|
+
provider: 'smartdb';
|
|
248
|
+
resourceIdSha256: string;
|
|
249
|
+
holdPublication: true;
|
|
250
|
+
publicationState: 'held';
|
|
251
|
+
bindingSha256: string;
|
|
252
|
+
resultSha256: string;
|
|
253
|
+
receiptSha256: string;
|
|
254
|
+
publicationCapabilitySha256: string;
|
|
255
|
+
publicationCapability: string;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
export interface INoSqlDbCommitDatabasePublicationInput {
|
|
259
|
+
databaseName: string;
|
|
260
|
+
resourceFence: INoSqlDbHeldPublicationReceipt;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Cancellation and deadline ownership for one durable database-management
|
|
265
|
+
* operation. Aborting or timing out is fail-stop: NoSQLDB terminates the Rust
|
|
266
|
+
* engine so the operation cannot continue after the caller has released it.
|
|
267
|
+
*/
|
|
268
|
+
export interface INoSqlDbManagementOperationOptions {
|
|
269
|
+
signal?: AbortSignal;
|
|
270
|
+
timeoutMs?: number;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
export interface INoSqlDbDatabaseResourceFenceState {
|
|
274
|
+
version: 1;
|
|
275
|
+
databaseName: string;
|
|
276
|
+
resourceIdSha256: string;
|
|
277
|
+
scopeId: string;
|
|
278
|
+
highestToken: number;
|
|
279
|
+
publicationState: 'requested' | 'publishing' | 'held' | 'released';
|
|
280
|
+
allocation?: INoSqlDbDatabaseAllocationLifecycle;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
export class NoSqlDbResourceFenceError extends Error {
|
|
284
|
+
public readonly code: TNoSqlDbResourceFenceErrorCode;
|
|
285
|
+
|
|
286
|
+
public constructor(
|
|
287
|
+
codeArg: TNoSqlDbResourceFenceErrorCode,
|
|
288
|
+
messageArg: string,
|
|
289
|
+
causeArg?: unknown,
|
|
290
|
+
) {
|
|
291
|
+
super(`${codeArg}: ${messageArg}`, { cause: causeArg });
|
|
292
|
+
this.name = 'NoSqlDbResourceFenceError';
|
|
293
|
+
this.code = codeArg;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
export const normalizeNoSqlDbResourceFenceError = (
|
|
298
|
+
errorArg: unknown,
|
|
299
|
+
): Error => {
|
|
300
|
+
const error =
|
|
301
|
+
errorArg instanceof Error
|
|
302
|
+
? errorArg
|
|
303
|
+
: new Error(String(errorArg));
|
|
304
|
+
const match = /^((?:EFENCE|EALLOCATION)_[A-Z_]+):\s*(.*)$/s.exec(error.message);
|
|
305
|
+
if (
|
|
306
|
+
!match ||
|
|
307
|
+
!noSqlDbResourceFenceErrorCodes.includes(
|
|
308
|
+
match[1] as TNoSqlDbResourceFenceErrorCode,
|
|
309
|
+
)
|
|
310
|
+
) {
|
|
311
|
+
return error;
|
|
312
|
+
}
|
|
313
|
+
return new NoSqlDbResourceFenceError(
|
|
314
|
+
match[1] as TNoSqlDbResourceFenceErrorCode,
|
|
315
|
+
match[2],
|
|
316
|
+
error,
|
|
317
|
+
);
|
|
318
|
+
};
|
|
319
|
+
|
|
320
|
+
export interface INoSqlDbDatabaseTenantInput {
|
|
321
|
+
databaseName: string;
|
|
322
|
+
username: string;
|
|
323
|
+
password: string;
|
|
324
|
+
roles?: string[];
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
export interface INoSqlDbEnsureDatabaseTenantInput
|
|
328
|
+
extends INoSqlDbDatabaseTenantInput {
|
|
329
|
+
fence: INoSqlDbResourceFence;
|
|
330
|
+
allocation?: INoSqlDbDatabaseAllocationIdentity;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
export interface INoSqlDbAttestDatabaseTenantInput {
|
|
334
|
+
databaseName: string;
|
|
335
|
+
username: string;
|
|
336
|
+
roles: string[];
|
|
337
|
+
password: string;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
export interface INoSqlDbAttestDatabaseTenantResult {
|
|
341
|
+
matches: boolean;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
export interface INoSqlDbAllocateDatabaseTenantInput
|
|
345
|
+
extends INoSqlDbDatabaseTenantInput {
|
|
346
|
+
expectedAbsent: true;
|
|
347
|
+
fence: INoSqlDbResourceFence;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
export interface INoSqlDbDeleteDatabaseTenantInput {
|
|
351
|
+
databaseName: string;
|
|
352
|
+
/**
|
|
353
|
+
* Exact tenant user to revoke. For a fenced deletion this identity is
|
|
354
|
+
* included in the durable mutation and is removed idempotently after the
|
|
355
|
+
* database deletion is durable. Omitting it from a fenced deletion
|
|
356
|
+
* preserves auth users; an unfenced omission retains the legacy behavior
|
|
357
|
+
* of deleting every user for the database. If the database exists, a
|
|
358
|
+
* fenced deletion fails closed unless this exact user currently owns it.
|
|
359
|
+
*/
|
|
360
|
+
username?: string;
|
|
361
|
+
fence?: INoSqlDbResourceFence;
|
|
362
|
+
allocation?: INoSqlDbDatabaseAllocationIdentity;
|
|
363
|
+
/** Keep the database data plane closed until commitDatabasePublication. */
|
|
364
|
+
holdPublication?: boolean;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
export interface INoSqlDbRotateDatabaseTenantPasswordInput {
|
|
368
|
+
username: string;
|
|
369
|
+
password: string;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
export interface INoSqlDbDatabaseTenantDescriptor {
|
|
373
|
+
databaseName: string;
|
|
374
|
+
username: string;
|
|
375
|
+
roles: string[];
|
|
376
|
+
authSource: string;
|
|
377
|
+
mongodbUri?: string;
|
|
378
|
+
resourceFence?: INoSqlDbResourceFenceReceipt;
|
|
379
|
+
allocation?: INoSqlDbDatabaseAllocationIdentity;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
export interface INoSqlDbEnsureDatabaseTenantResult
|
|
383
|
+
extends INoSqlDbDatabaseTenantDescriptor {
|
|
384
|
+
resourceFence: INoSqlDbResourceFenceReceipt;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
export interface INoSqlDbAllocateDatabaseTenantResult
|
|
388
|
+
extends INoSqlDbDatabaseTenantDescriptor {
|
|
389
|
+
resourceFence: INoSqlDbResourceFenceReceipt;
|
|
390
|
+
allocation: INoSqlDbDatabaseAllocationIdentity;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
export interface INoSqlDbDeleteDatabaseTenantResult {
|
|
394
|
+
databaseName: string;
|
|
395
|
+
/**
|
|
396
|
+
* Users removed during the completing attempt. This can be zero after
|
|
397
|
+
* crash recovery when the exact user was durably removed by the prior
|
|
398
|
+
* attempt.
|
|
399
|
+
*/
|
|
400
|
+
deletedUsers: number;
|
|
401
|
+
databaseDropped: boolean;
|
|
402
|
+
/**
|
|
403
|
+
* Present for fenced calls. False means the bound exact username is now
|
|
404
|
+
* absent; true means no username was supplied and auth users were preserved.
|
|
405
|
+
*/
|
|
406
|
+
usersPreserved?: boolean;
|
|
407
|
+
resourceFence?: INoSqlDbResourceFenceReceipt;
|
|
408
|
+
allocation?: INoSqlDbDatabaseAllocationIdentity;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
export interface INoSqlDbDatabaseExportCollection {
|
|
412
|
+
name: string;
|
|
413
|
+
documents: Record<string, any>[];
|
|
414
|
+
indexes: Record<string, any>[];
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
export interface INoSqlDbDatabaseExportLimits {
|
|
418
|
+
maxEncodedBytes?: number;
|
|
419
|
+
maxCollections?: number;
|
|
420
|
+
maxDocuments?: number;
|
|
421
|
+
maxIndexes?: number;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
export interface INoSqlDbExportDatabaseInput {
|
|
425
|
+
databaseName: string;
|
|
426
|
+
limits?: INoSqlDbDatabaseExportLimits;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
export interface INoSqlDbDatabaseContentDigestLimits {
|
|
430
|
+
maxScannedBsonBytes?: number;
|
|
431
|
+
maxCollections?: number;
|
|
432
|
+
maxDocuments?: number;
|
|
433
|
+
maxIndexes?: number;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
export interface INoSqlDbGetDatabaseContentDigestInput {
|
|
437
|
+
databaseName: string;
|
|
438
|
+
limits?: INoSqlDbDatabaseContentDigestLimits;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
export interface INoSqlDbDatabaseContentDigest {
|
|
442
|
+
format: 'smartdb.database.content-digest.v1';
|
|
443
|
+
algorithm: 'sha256';
|
|
444
|
+
databaseName: string;
|
|
445
|
+
sha256: string;
|
|
446
|
+
scannedBsonBytes: number;
|
|
447
|
+
collections: number;
|
|
448
|
+
documents: number;
|
|
449
|
+
indexes: number;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
export interface INoSqlDbDatabaseExport {
|
|
453
|
+
format: 'smartdb.database.export.v1';
|
|
454
|
+
databaseName: string;
|
|
455
|
+
exportedAtMs: number;
|
|
456
|
+
collections: INoSqlDbDatabaseExportCollection[];
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
export interface INoSqlDbImportDatabaseInput {
|
|
460
|
+
databaseName: string;
|
|
461
|
+
source: INoSqlDbDatabaseExport;
|
|
462
|
+
/**
|
|
463
|
+
* Exact exclusive tenant owner required for this replacement. When used
|
|
464
|
+
* with a fence, the username is included in the provider-derived mutation
|
|
465
|
+
* identity. Omission preserves the original version 1 replacement profile.
|
|
466
|
+
*/
|
|
467
|
+
username?: string;
|
|
468
|
+
fence?: INoSqlDbResourceFence;
|
|
469
|
+
allocation?: INoSqlDbDatabaseAllocationIdentity;
|
|
470
|
+
/** Keep the database data plane closed until commitDatabasePublication. */
|
|
471
|
+
holdPublication?: boolean;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
export interface INoSqlDbImportDatabaseResult {
|
|
475
|
+
databaseName: string;
|
|
476
|
+
collections: number;
|
|
477
|
+
documents: number;
|
|
478
|
+
resourceFence?: INoSqlDbResourceFenceReceipt;
|
|
479
|
+
allocation?: INoSqlDbDatabaseAllocationIdentity;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
export type TNoSqlDbCommitDatabasePublicationResult =
|
|
483
|
+
| INoSqlDbImportDatabaseResult
|
|
484
|
+
| INoSqlDbDeleteDatabaseTenantResult;
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import * as plugins from './plugins.js';
|
|
2
|
+
import type { INoSqlDbServerOptions } from './server/classes.nosqldbserver.js';
|
|
3
|
+
|
|
4
|
+
export const noSqlDbStorageRehearsalErrorCodes = [
|
|
5
|
+
'INVALID_REQUEST', 'UNSUPPORTED', 'BUSY', 'NOT_FOUND', 'DESTINATION_CONFLICT',
|
|
6
|
+
'IDENTITY_MISMATCH', 'STATE_CORRUPT', 'SOURCE_CHANGED', 'CAPACITY', 'CANCELLED',
|
|
7
|
+
'IO', 'RECOVERY_REQUIRED', 'BINARY_UNAVAILABLE',
|
|
8
|
+
] as const;
|
|
9
|
+
export type TNoSqlDbStorageRehearsalErrorCode = typeof noSqlDbStorageRehearsalErrorCodes[number];
|
|
10
|
+
|
|
11
|
+
/** Errors deliberately contain neither persisted values nor nested engine errors. */
|
|
12
|
+
export class NoSqlDbStorageRehearsalError extends Error {
|
|
13
|
+
public constructor(public readonly code: TNoSqlDbStorageRehearsalErrorCode) {
|
|
14
|
+
super(`NoSQLDB storage rehearsal failed with code ${code}`);
|
|
15
|
+
this.name = 'NoSqlDbStorageRehearsalError';
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface INoSqlDbStorageRehearsalOptions {
|
|
20
|
+
/** Exact executable, with SmartRust's exclusive non-mutating selection contract. */
|
|
21
|
+
binaryPath?: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface INoSqlDbStorageRehearsalInput {
|
|
25
|
+
/** Absolute canonical path of an already initialized, stopped source. */
|
|
26
|
+
sourceFolderPath: string;
|
|
27
|
+
/** Absent destination; its existing parent must be private and on the source filesystem. */
|
|
28
|
+
destinationFolderPath: string;
|
|
29
|
+
/** Optional previously attested provider identity. The engine always verifies source ownership and bindings. */
|
|
30
|
+
expectedSourceProviderRootId?: string;
|
|
31
|
+
/** Stable caller operation ID. Every retry must retain the same request. */
|
|
32
|
+
operationId: string;
|
|
33
|
+
limits?: {
|
|
34
|
+
/** Source files plus directories, including the root. Default/maximum: 100,000. */
|
|
35
|
+
maximumEntries: number;
|
|
36
|
+
/** Total source file bytes. Default/maximum: 1 TiB. */
|
|
37
|
+
maximumBytes: number;
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Counts include retained historical records separately from live contents. */
|
|
42
|
+
export interface INoSqlDbStorageRehearsalPreservation {
|
|
43
|
+
files: number;
|
|
44
|
+
bytes: number;
|
|
45
|
+
databases: number;
|
|
46
|
+
collections: number;
|
|
47
|
+
documents: number;
|
|
48
|
+
indexes: number;
|
|
49
|
+
indexAliases: number;
|
|
50
|
+
users: number;
|
|
51
|
+
principalIdentities: number;
|
|
52
|
+
retiredDatabases: number;
|
|
53
|
+
retiredCollections: number;
|
|
54
|
+
retiredDocuments: number;
|
|
55
|
+
retiredIndexes: number;
|
|
56
|
+
retiredPrincipals: number;
|
|
57
|
+
allocationReplays: number;
|
|
58
|
+
resources: number;
|
|
59
|
+
receipts: number;
|
|
60
|
+
maximumFencingToken: string;
|
|
61
|
+
publicationHolds: number;
|
|
62
|
+
activeAllocations: number;
|
|
63
|
+
retiredAllocations: number;
|
|
64
|
+
sessions: number;
|
|
65
|
+
retiringSessions: number;
|
|
66
|
+
retryOutcomes: number;
|
|
67
|
+
transactionOutcomes: number;
|
|
68
|
+
obsoleteOutcomes: number;
|
|
69
|
+
records: number;
|
|
70
|
+
encodedRecordBytes: number;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** Immutable creation evidence. Completed retries replay this receipt even after isolated writes. */
|
|
74
|
+
export interface INoSqlDbStorageRehearsalReceipt {
|
|
75
|
+
format: 'smartdb.storage-rehearsal.receipt.v2';
|
|
76
|
+
operationId: string;
|
|
77
|
+
requestSha256: string;
|
|
78
|
+
storageProfile: 'native-mvcc-authenticated-v1';
|
|
79
|
+
sourceFolderPath: string;
|
|
80
|
+
destinationFolderPath: string;
|
|
81
|
+
sourceProviderRootId: string;
|
|
82
|
+
destinationProviderRootId: string;
|
|
83
|
+
sourceStorageRootSha256: string;
|
|
84
|
+
sourceSnapshotSha256: string;
|
|
85
|
+
destinationDevice: string;
|
|
86
|
+
destinationInode: string;
|
|
87
|
+
groupId: string;
|
|
88
|
+
timelineId: string;
|
|
89
|
+
sourceLsn: string;
|
|
90
|
+
destinationLsn: string;
|
|
91
|
+
sourceRecordsSha256: string;
|
|
92
|
+
preservedRecordsSha256: string;
|
|
93
|
+
scramIterations: number;
|
|
94
|
+
preservation: INoSqlDbStorageRehearsalPreservation;
|
|
95
|
+
sourceUnchanged: true;
|
|
96
|
+
contentsAndIndexesPreserved: true;
|
|
97
|
+
namespacesPreserved: true;
|
|
98
|
+
authenticationPreserved: true;
|
|
99
|
+
historicalFencingPreserved: true;
|
|
100
|
+
outcomesPreserved: true;
|
|
101
|
+
filesystemBindingsVerified: true;
|
|
102
|
+
productionAuthority: false;
|
|
103
|
+
promotionSupported: false;
|
|
104
|
+
receiptSha256: string;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export type TNormalizedNoSqlDbStorageRehearsalInput = INoSqlDbStorageRehearsalInput & {
|
|
108
|
+
limits: NonNullable<INoSqlDbStorageRehearsalInput['limits']>;
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
const fail = (code: TNoSqlDbStorageRehearsalErrorCode = 'INVALID_REQUEST'): never => {
|
|
112
|
+
throw new NoSqlDbStorageRehearsalError(code);
|
|
113
|
+
};
|
|
114
|
+
const object = (value: unknown): value is Record<string, unknown> => {
|
|
115
|
+
try {
|
|
116
|
+
if (value === null || typeof value !== 'object' || Array.isArray(value)
|
|
117
|
+
|| ![Object.prototype, null].includes(Object.getPrototypeOf(value))) return false;
|
|
118
|
+
return Reflect.ownKeys(value).every((key) => {
|
|
119
|
+
const property = Object.getOwnPropertyDescriptor(value, key);
|
|
120
|
+
return typeof key === 'string' && property?.enumerable === true && Object.hasOwn(property, 'value');
|
|
121
|
+
});
|
|
122
|
+
} catch { return false; }
|
|
123
|
+
};
|
|
124
|
+
const digest = (value: unknown): value is string => typeof value === 'string' && /^[0-9a-f]{64}$/.test(value);
|
|
125
|
+
const identifier = (value: unknown): value is string => typeof value === 'string'
|
|
126
|
+
&& /^[A-Za-z0-9][A-Za-z0-9._:@-]{0,127}$/.test(value);
|
|
127
|
+
const canonicalPath = (value: unknown): value is string => typeof value === 'string'
|
|
128
|
+
&& value.startsWith('/') && value !== '/' && plugins.buffer.Buffer.byteLength(value) <= 4095
|
|
129
|
+
&& !/[\p{Cc}\p{Cs}]/u.test(value) && !value.endsWith('/') && !value.includes('//')
|
|
130
|
+
&& value.split('/').every((part) => part !== '.' && part !== '..' && !part.startsWith('.smartdb-rehearsal-'));
|
|
131
|
+
const integer = (value: unknown, minimum: number, maximum: number): value is number =>
|
|
132
|
+
typeof value === 'number' && Number.isSafeInteger(value) && value >= minimum && value <= maximum;
|
|
133
|
+
const exactKeys = (value: Record<string, unknown>, required: readonly string[], optional: readonly string[] = []) =>
|
|
134
|
+
required.every((key) => Object.hasOwn(value, key)) && Object.keys(value).every((key) => required.includes(key) || optional.includes(key));
|
|
135
|
+
const sha256 = (domain: string, value: unknown) => plugins.crypto.createHash('sha256').update(domain).update(JSON.stringify(value)).digest('hex');
|
|
136
|
+
|
|
137
|
+
export const normalizeNoSqlDbStorageRehearsalOptions = (value: unknown): INoSqlDbStorageRehearsalOptions => {
|
|
138
|
+
try {
|
|
139
|
+
if (!object(value) || !exactKeys(value, [], ['binaryPath'])
|
|
140
|
+
|| (value.binaryPath !== undefined && (typeof value.binaryPath !== 'string' || value.binaryPath.length === 0))) return fail();
|
|
141
|
+
return { binaryPath: value.binaryPath as string | undefined };
|
|
142
|
+
} catch { return fail(); }
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
export const normalizeNoSqlDbStorageRehearsalOperationOptions = (value: unknown): { timeoutMs: number; signal?: AbortSignal } => {
|
|
146
|
+
try {
|
|
147
|
+
if (value === undefined) return { timeoutMs: 3_600_000 };
|
|
148
|
+
if (!object(value) || !exactKeys(value, [], ['timeoutMs', 'signal'])
|
|
149
|
+
|| (value.timeoutMs !== undefined && !integer(value.timeoutMs, 1, 86_400_000))
|
|
150
|
+
|| (value.signal !== undefined && !(value.signal instanceof AbortSignal))) return fail();
|
|
151
|
+
return { timeoutMs: value.timeoutMs as number | undefined ?? 3_600_000, signal: value.signal as AbortSignal | undefined };
|
|
152
|
+
} catch { return fail(); }
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
export const normalizeNoSqlDbStorageRehearsalInput = (value: unknown): TNormalizedNoSqlDbStorageRehearsalInput => {
|
|
156
|
+
if (!object(value) || !exactKeys(value, ['sourceFolderPath', 'destinationFolderPath', 'operationId'], ['expectedSourceProviderRootId', 'limits'])
|
|
157
|
+
|| !canonicalPath(value.sourceFolderPath) || !canonicalPath(value.destinationFolderPath)
|
|
158
|
+
|| value.sourceFolderPath === value.destinationFolderPath
|
|
159
|
+
|| value.sourceFolderPath.startsWith(`${value.destinationFolderPath}/`)
|
|
160
|
+
|| value.destinationFolderPath.startsWith(`${value.sourceFolderPath}/`)
|
|
161
|
+
|| (value.expectedSourceProviderRootId !== undefined && !digest(value.expectedSourceProviderRootId))
|
|
162
|
+
|| !identifier(value.operationId)) return fail();
|
|
163
|
+
const limits = value.limits === undefined ? { maximumEntries: 100_000, maximumBytes: 1024 ** 4 } : value.limits;
|
|
164
|
+
if (!object(limits) || !exactKeys(limits, ['maximumEntries', 'maximumBytes'])
|
|
165
|
+
|| !integer(limits.maximumEntries, 1, 100_000) || !integer(limits.maximumBytes, 1, 1024 ** 4)) return fail();
|
|
166
|
+
return {
|
|
167
|
+
sourceFolderPath: value.sourceFolderPath,
|
|
168
|
+
destinationFolderPath: value.destinationFolderPath,
|
|
169
|
+
...(value.expectedSourceProviderRootId === undefined ? {} : { expectedSourceProviderRootId: value.expectedSourceProviderRootId as string }),
|
|
170
|
+
operationId: value.operationId,
|
|
171
|
+
limits: { maximumEntries: limits.maximumEntries, maximumBytes: limits.maximumBytes },
|
|
172
|
+
};
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
const preservationKeys = ['files', 'bytes', 'databases', 'collections', 'documents', 'indexes', 'indexAliases', 'users', 'principalIdentities', 'retiredDatabases', 'retiredCollections', 'retiredDocuments', 'retiredIndexes', 'retiredPrincipals', 'allocationReplays', 'resources', 'receipts', 'maximumFencingToken', 'publicationHolds', 'activeAllocations', 'retiredAllocations', 'sessions', 'retiringSessions', 'retryOutcomes', 'transactionOutcomes', 'obsoleteOutcomes', 'records', 'encodedRecordBytes'] as const;
|
|
176
|
+
const receiptKeys = ['format', 'operationId', 'requestSha256', 'storageProfile', 'sourceFolderPath', 'destinationFolderPath', 'sourceProviderRootId', 'destinationProviderRootId', 'sourceStorageRootSha256', 'sourceSnapshotSha256', 'destinationDevice', 'destinationInode', 'groupId', 'timelineId', 'sourceLsn', 'destinationLsn', 'sourceRecordsSha256', 'preservedRecordsSha256', 'scramIterations', 'preservation', 'sourceUnchanged', 'contentsAndIndexesPreserved', 'namespacesPreserved', 'authenticationPreserved', 'historicalFencingPreserved', 'outcomesPreserved', 'filesystemBindingsVerified', 'productionAuthority', 'promotionSupported', 'receiptSha256'] as const;
|
|
177
|
+
const unsigned = (value: unknown): value is string => typeof value === 'string'
|
|
178
|
+
&& /^(0|[1-9][0-9]{0,19})$/.test(value) && BigInt(value) <= 0xffff_ffff_ffff_ffffn;
|
|
179
|
+
|
|
180
|
+
export const normalizeNoSqlDbStorageRehearsalReceipt = (value: unknown, input: TNormalizedNoSqlDbStorageRehearsalInput): INoSqlDbStorageRehearsalReceipt => {
|
|
181
|
+
if (!object(value) || !exactKeys(value, receiptKeys) || !object(value.preservation)
|
|
182
|
+
|| !exactKeys(value.preservation, preservationKeys)
|
|
183
|
+
|| !preservationKeys.every((key) => key === 'maximumFencingToken'
|
|
184
|
+
? unsigned((value.preservation as Record<string, unknown>)[key])
|
|
185
|
+
: integer((value.preservation as Record<string, unknown>)[key], 0, Number.MAX_SAFE_INTEGER))
|
|
186
|
+
|| value.format !== 'smartdb.storage-rehearsal.receipt.v2' || value.storageProfile !== 'native-mvcc-authenticated-v1'
|
|
187
|
+
|| value.operationId !== input.operationId || value.sourceFolderPath !== input.sourceFolderPath
|
|
188
|
+
|| value.destinationFolderPath !== input.destinationFolderPath
|
|
189
|
+
|| (input.expectedSourceProviderRootId !== undefined && value.sourceProviderRootId !== input.expectedSourceProviderRootId)
|
|
190
|
+
|| !['sourceProviderRootId', 'destinationProviderRootId', 'sourceSnapshotSha256', 'sourceRecordsSha256', 'preservedRecordsSha256', 'receiptSha256'].every((key) => digest(value[key]))
|
|
191
|
+
|| value.sourceProviderRootId === value.destinationProviderRootId
|
|
192
|
+
|| value.sourceStorageRootSha256 !== plugins.crypto.createHash('sha256').update(`${input.sourceFolderPath}/.__rustdb_mvcc/data`).digest('hex')
|
|
193
|
+
|| value.requestSha256 !== sha256('smartdb.storage-rehearsal.request.v2\0', input)
|
|
194
|
+
|| !integer(value.scramIterations, 4096, 0xffff_ffff)
|
|
195
|
+
|| ![value.destinationDevice, value.destinationInode, value.sourceLsn, value.destinationLsn].every(unsigned)
|
|
196
|
+
|| BigInt(value.sourceLsn as string) < 1n || BigInt(value.sourceLsn as string) + 1n !== BigInt(value.destinationLsn as string)
|
|
197
|
+
|| !identifier(value.groupId) || !identifier(value.timelineId)
|
|
198
|
+
|| !['sourceUnchanged', 'contentsAndIndexesPreserved', 'namespacesPreserved', 'authenticationPreserved', 'historicalFencingPreserved', 'outcomesPreserved', 'filesystemBindingsVerified'].every((key) => value[key] === true)
|
|
199
|
+
|| value.productionAuthority !== false || value.promotionSupported !== false) return fail('STATE_CORRUPT');
|
|
200
|
+
const preservation = Object.fromEntries(preservationKeys.map((key) => [key, (value.preservation as Record<string, unknown>)[key]]));
|
|
201
|
+
const normalized = Object.fromEntries(receiptKeys.map((key) => [key,
|
|
202
|
+
key === 'preservation' ? preservation : key === 'receiptSha256' ? '' : value[key]]));
|
|
203
|
+
if (sha256('smartdb.storage-rehearsal.receipt.v2\0', normalized) !== value.receiptSha256) return fail('STATE_CORRUPT');
|
|
204
|
+
normalized.receiptSha256 = value.receiptSha256;
|
|
205
|
+
return normalized as unknown as INoSqlDbStorageRehearsalReceipt;
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
export const validateNoSqlDbStorageRehearsalStartup = (options: INoSqlDbServerOptions): void => {
|
|
209
|
+
if (options.storageRehearsalId === undefined) return;
|
|
210
|
+
if (!identifier(options.storageRehearsalId) || options.storage !== 'file'
|
|
211
|
+
|| (options.fileStorageStartup !== undefined && options.fileStorageStartup !== 'current-format-only') || !canonicalPath(options.storagePath)
|
|
212
|
+
|| !canonicalPath(options.socketPath) || options.socketPath.startsWith(`${options.storagePath}/`)
|
|
213
|
+
|| options.auth?.enabled !== true || (options.auth.users?.length ?? 0) !== 0
|
|
214
|
+
|| !integer(options.auth.scramIterations, 4096, 0xffff_ffff)) return fail();
|
|
215
|
+
};
|