@lossless.org/client 1.6.0 → 1.8.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/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/nosqldb/classes.atomicdelete.d.ts +1 -0
- package/dist_ts/nosqldb/classes.atomicdelete.js +10 -7
- package/dist_ts/nosqldb/classes.atomicfindoneandupdate.d.ts +2 -0
- package/dist_ts/nosqldb/classes.atomicfindoneandupdate.js +9 -5
- package/dist_ts/nosqldb/classes.atomicupdate.d.ts +2 -0
- package/dist_ts/nosqldb/classes.atomicupdate.js +13 -8
- package/dist_ts/nosqldb/classes.collection.d.ts +13 -4
- package/dist_ts/nosqldb/classes.collection.js +57 -64
- package/dist_ts/nosqldb/classes.db.d.ts +1 -1
- package/dist_ts/nosqldb/classes.doc.d.ts +60 -3
- package/dist_ts/nosqldb/classes.doc.js +68 -26
- package/dist_ts/nosqldb/classes.exactpersistence.d.ts +36 -7
- package/dist_ts/nosqldb/classes.exactpersistence.js +92 -81
- package/dist_ts/nosqldb/classes.operationdeadline.d.ts +61 -0
- package/dist_ts/nosqldb/classes.operationdeadline.js +149 -0
- package/dist_ts/nosqldb/classes.persistence.d.ts +16 -1
- package/dist_ts/nosqldb/classes.persistence.js +20 -1
- package/dist_ts/nosqldb/classes.session.d.ts +73 -2
- package/dist_ts/nosqldb/classes.session.js +164 -38
- package/dist_ts/objectstorage/classes.bucket.d.ts +61 -3
- package/dist_ts/objectstorage/classes.bucket.js +243 -184
- package/dist_ts/objectstorage/classes.directory.js +17 -28
- package/dist_ts/objectstorage/classes.smartbucket.d.ts +13 -6
- package/dist_ts/objectstorage/classes.smartbucket.js +18 -12
- package/dist_ts/objectstorage/classes.watcher.js +1 -2
- package/dist_ts/objectstorage/interfaces.d.ts +8 -0
- package/dist_ts/objectstorage/internal.bucketrequests.d.ts +14 -0
- package/dist_ts/objectstorage/internal.bucketrequests.js +53 -0
- package/dist_ts/objectstorage/internal.exactupload.capability.d.ts +12 -0
- package/dist_ts/objectstorage/internal.exactupload.capability.js +46 -17
- package/package.json +3 -3
- package/readme.md +13 -2
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/nosqldb/classes.atomicdelete.ts +10 -6
- package/ts/nosqldb/classes.atomicfindoneandupdate.ts +10 -4
- package/ts/nosqldb/classes.atomicupdate.ts +14 -7
- package/ts/nosqldb/classes.collection.ts +82 -73
- package/ts/nosqldb/classes.db.ts +1 -1
- package/ts/nosqldb/classes.doc.ts +162 -33
- package/ts/nosqldb/classes.exactpersistence.ts +131 -93
- package/ts/nosqldb/classes.operationdeadline.ts +179 -0
- package/ts/nosqldb/classes.persistence.ts +36 -1
- package/ts/nosqldb/classes.session.ts +221 -39
- package/ts/objectstorage/classes.bucket.ts +350 -218
- package/ts/objectstorage/classes.directory.ts +18 -27
- package/ts/objectstorage/classes.smartbucket.ts +21 -11
- package/ts/objectstorage/classes.watcher.ts +0 -1
- package/ts/objectstorage/interfaces.ts +9 -0
- package/ts/objectstorage/internal.bucketrequests.ts +70 -0
- package/ts/objectstorage/internal.exactupload.capability.ts +105 -24
|
@@ -179,30 +179,25 @@ export class Directory {
|
|
|
179
179
|
* lists all folders
|
|
180
180
|
*/
|
|
181
181
|
public async listDirectories(): Promise<Directory[]> {
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
directoryArray.push(new Directory(this.bucketRef, this, dirName));
|
|
195
|
-
}
|
|
182
|
+
const { commonPrefixes } = await this.listObjectsV2AllPages(this.getBasePath(), '/');
|
|
183
|
+
const directoryArray: Directory[] = [];
|
|
184
|
+
|
|
185
|
+
if (commonPrefixes) {
|
|
186
|
+
commonPrefixes.forEach((item) => {
|
|
187
|
+
if (item.Prefix) {
|
|
188
|
+
const subtractedPath = item.Prefix.replace(this.getBasePath(), '');
|
|
189
|
+
if (subtractedPath.endsWith('/')) {
|
|
190
|
+
const dirName = subtractedPath.slice(0, -1);
|
|
191
|
+
// Ensure the directory name is not empty (which would indicate the base directory itself)
|
|
192
|
+
if (dirName) {
|
|
193
|
+
directoryArray.push(new Directory(this.bucketRef, this, dirName));
|
|
196
194
|
}
|
|
197
195
|
}
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
return directoryArray;
|
|
202
|
-
} catch (error) {
|
|
203
|
-
console.error('Error listing directories:', error);
|
|
204
|
-
throw error;
|
|
196
|
+
}
|
|
197
|
+
});
|
|
205
198
|
}
|
|
199
|
+
|
|
200
|
+
return directoryArray;
|
|
206
201
|
}
|
|
207
202
|
|
|
208
203
|
/**
|
|
@@ -409,12 +404,8 @@ export class Directory {
|
|
|
409
404
|
}) {
|
|
410
405
|
const deleteDirectory = async (directoryArg: Directory) => {
|
|
411
406
|
const childDirectories = await directoryArg.listDirectories();
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
} else {
|
|
415
|
-
for (const childDir of childDirectories) {
|
|
416
|
-
await deleteDirectory(childDir);
|
|
417
|
-
}
|
|
407
|
+
for (const childDir of childDirectories) {
|
|
408
|
+
await deleteDirectory(childDir);
|
|
418
409
|
}
|
|
419
410
|
const files = await directoryArg.listFiles();
|
|
420
411
|
for (const file of files) {
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import * as plugins from './plugins.js';
|
|
4
4
|
import { Bucket } from './classes.bucket.js';
|
|
5
5
|
import { normalizeStorageDescriptor } from './helpers.js';
|
|
6
|
+
import { headBucketExists } from './internal.bucketrequests.js';
|
|
6
7
|
import {
|
|
7
8
|
beginOperationLifecycleClose,
|
|
8
9
|
commitOperationLifecycleClient,
|
|
@@ -12,6 +13,7 @@ import {
|
|
|
12
13
|
rollbackOperationLifecycleClientMutation,
|
|
13
14
|
} from './internal.exactupload.capability.js';
|
|
14
15
|
import type {
|
|
16
|
+
IBucketRequestOptions,
|
|
15
17
|
ISmartBucketReadinessOptions,
|
|
16
18
|
TSmartBucketReadinessFailureReason,
|
|
17
19
|
TSmartBucketReadinessResult,
|
|
@@ -107,26 +109,34 @@ export class SmartBucket {
|
|
|
107
109
|
registerOperationLifecycle(this, this.currentStorageClient);
|
|
108
110
|
}
|
|
109
111
|
|
|
110
|
-
public async createBucket(bucketNameArg: string) {
|
|
111
|
-
const bucket = await Bucket.createBucketByName(this, bucketNameArg);
|
|
112
|
+
public async createBucket(bucketNameArg: string, optionsArg: IBucketRequestOptions = {}) {
|
|
113
|
+
const bucket = await Bucket.createBucketByName(this, bucketNameArg, optionsArg);
|
|
112
114
|
return bucket;
|
|
113
115
|
}
|
|
114
116
|
|
|
115
|
-
public async removeBucket(bucketName: string) {
|
|
116
|
-
await Bucket.removeBucketByName(this, bucketName);
|
|
117
|
+
public async removeBucket(bucketName: string, optionsArg: IBucketRequestOptions = {}) {
|
|
118
|
+
await Bucket.removeBucketByName(this, bucketName, optionsArg);
|
|
117
119
|
}
|
|
118
120
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
+
/**
|
|
122
|
+
* Returns a reference to an existing bucket, checked with one HeadBucket
|
|
123
|
+
* request. Rejects when the bucket does not exist or the credentials may
|
|
124
|
+
* not access it.
|
|
125
|
+
*/
|
|
126
|
+
public async getBucketByName(bucketNameArg: string, optionsArg: IBucketRequestOptions = {}) {
|
|
127
|
+
return Bucket.getBucketByName(this, bucketNameArg, optionsArg);
|
|
121
128
|
}
|
|
122
129
|
|
|
123
130
|
/**
|
|
124
|
-
*
|
|
131
|
+
* Checks with one HeadBucket request whether a bucket exists. Resolves
|
|
132
|
+
* `false` for a missing bucket and rejects when the credentials may not
|
|
133
|
+
* access it.
|
|
125
134
|
*/
|
|
126
|
-
public async bucketExists(
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
135
|
+
public async bucketExists(
|
|
136
|
+
bucketNameArg: string,
|
|
137
|
+
optionsArg: IBucketRequestOptions = {},
|
|
138
|
+
): Promise<boolean> {
|
|
139
|
+
return headBucketExists(this, bucketNameArg, optionsArg.signal);
|
|
130
140
|
}
|
|
131
141
|
|
|
132
142
|
/**
|
|
@@ -52,6 +52,15 @@ export interface IDirectoryFastPutStreamOptions {
|
|
|
52
52
|
signal?: AbortSignal;
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
+
/**
|
|
56
|
+
* Options for a request about a bucket itself: `getBucketByName()`,
|
|
57
|
+
* `bucketExists()`, `createBucket()` and `removeBucket()`.
|
|
58
|
+
*/
|
|
59
|
+
export interface IBucketRequestOptions {
|
|
60
|
+
/** Optional caller-owned cancellation signal */
|
|
61
|
+
signal?: AbortSignal;
|
|
62
|
+
}
|
|
63
|
+
|
|
55
64
|
// ================================
|
|
56
65
|
// SmartBucket Readiness Interfaces
|
|
57
66
|
// ================================
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import * as plugins from './plugins.js';
|
|
2
|
+
import type { SmartBucket } from './classes.smartbucket.js';
|
|
3
|
+
import { beginAccountOperation } from './internal.exactupload.capability.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Sends one account-scoped request under the SmartBucket lifecycle. The
|
|
7
|
+
* caller's signal and `SmartBucket.close()` abort the in-flight SDK request.
|
|
8
|
+
*/
|
|
9
|
+
export const runAccountRequest = async <T>(
|
|
10
|
+
smartbucketRefArg: SmartBucket,
|
|
11
|
+
signalArg: AbortSignal | undefined,
|
|
12
|
+
sendArg: (clientArg: plugins.s3.S3Client, abortSignalArg: AbortSignal) => Promise<T>,
|
|
13
|
+
): Promise<T> => {
|
|
14
|
+
const operation = beginAccountOperation(smartbucketRefArg, signalArg);
|
|
15
|
+
try {
|
|
16
|
+
return await operation.runProvider(() => sendArg(operation.client, operation.signal));
|
|
17
|
+
} finally {
|
|
18
|
+
operation.finish();
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const getProviderStatus = (errorArg: unknown): { name?: string; httpStatusCode?: number } => {
|
|
23
|
+
if (typeof errorArg !== 'object' || errorArg === null) {
|
|
24
|
+
return {};
|
|
25
|
+
}
|
|
26
|
+
const providerError = errorArg as {
|
|
27
|
+
name?: unknown;
|
|
28
|
+
$metadata?: { httpStatusCode?: unknown };
|
|
29
|
+
};
|
|
30
|
+
return {
|
|
31
|
+
name: typeof providerError.name === 'string' ? providerError.name : undefined,
|
|
32
|
+
httpStatusCode: typeof providerError.$metadata?.httpStatusCode === 'number'
|
|
33
|
+
? providerError.$metadata.httpStatusCode
|
|
34
|
+
: undefined,
|
|
35
|
+
};
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Answers whether the bucket exists with one HeadBucket request, which a
|
|
40
|
+
* bucket-scoped credential may send for its own bucket. A missing bucket
|
|
41
|
+
* resolves `false`; a bucket the credentials may not access rejects, because
|
|
42
|
+
* S3 does not say whether it exists.
|
|
43
|
+
*/
|
|
44
|
+
export const headBucketExists = async (
|
|
45
|
+
smartbucketRefArg: SmartBucket,
|
|
46
|
+
bucketNameArg: string,
|
|
47
|
+
signalArg: AbortSignal | undefined,
|
|
48
|
+
): Promise<boolean> => {
|
|
49
|
+
try {
|
|
50
|
+
await runAccountRequest(smartbucketRefArg, signalArg, (clientArg, abortSignalArg) =>
|
|
51
|
+
clientArg.send(new plugins.s3.HeadBucketCommand({ Bucket: bucketNameArg }), {
|
|
52
|
+
abortSignal: abortSignalArg,
|
|
53
|
+
}),
|
|
54
|
+
);
|
|
55
|
+
return true;
|
|
56
|
+
} catch (error) {
|
|
57
|
+
const { name, httpStatusCode } = getProviderStatus(error);
|
|
58
|
+
if (httpStatusCode === 404 || name === 'NotFound' || name === 'NoSuchBucket') {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
if (httpStatusCode === 403 || name === 'Forbidden' || name === 'AccessDenied') {
|
|
62
|
+
throw new Error(
|
|
63
|
+
`Access to bucket '${bucketNameArg}' was denied (HTTP 403): the credentials may not ` +
|
|
64
|
+
`read this bucket, or it belongs to another account.`,
|
|
65
|
+
{ cause: error },
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
throw error;
|
|
69
|
+
}
|
|
70
|
+
};
|
|
@@ -23,6 +23,8 @@ interface IOwnedIndependentAbort {
|
|
|
23
23
|
|
|
24
24
|
interface IActiveCapabilityOperation {
|
|
25
25
|
abortController: AbortController;
|
|
26
|
+
/** Abort cause when the SmartBucket closes while the operation is active. */
|
|
27
|
+
closedDuringMessage: string;
|
|
26
28
|
ownedIndependentAborts: Set<IOwnedIndependentAbort>;
|
|
27
29
|
settlement: Promise<void>;
|
|
28
30
|
settle: () => void;
|
|
@@ -38,14 +40,29 @@ interface ICapabilityLifecycleRecord {
|
|
|
38
40
|
clientMutationReserved: boolean;
|
|
39
41
|
}
|
|
40
42
|
|
|
41
|
-
interface
|
|
42
|
-
bucketRef: IBucketCapabilityRef;
|
|
43
|
-
bucketName: string;
|
|
43
|
+
interface ILifecycleBinding {
|
|
44
44
|
smartbucketRef: ISmartBucketCapabilityRef;
|
|
45
45
|
storageClient: plugins.s3.S3Client;
|
|
46
46
|
lifecycleGeneration: number;
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
+
interface IExactCapabilityBinding extends ILifecycleBinding {
|
|
50
|
+
bucketRef: IBucketCapabilityRef;
|
|
51
|
+
bucketName: string;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* What an operation is bound to beyond its lifecycle: a bucket reference that
|
|
56
|
+
* must keep its name and parent, or nothing for an account-scoped request.
|
|
57
|
+
* The messages describe the operation kind in lifecycle errors.
|
|
58
|
+
*/
|
|
59
|
+
interface IOperationIdentity {
|
|
60
|
+
isCurrent(): boolean;
|
|
61
|
+
staleMessage: string;
|
|
62
|
+
detachedMessage: string;
|
|
63
|
+
closedDuringMessage: string;
|
|
64
|
+
}
|
|
65
|
+
|
|
49
66
|
interface IExactPathPurgeCapabilityBinding extends IExactCapabilityBinding {
|
|
50
67
|
versioningMode: TExactPathPurgeVersioningMode;
|
|
51
68
|
}
|
|
@@ -76,6 +93,12 @@ export interface IExactPathPurgeOperation extends IExactUploadOperation {
|
|
|
76
93
|
readonly versioningMode: TExactPathPurgeVersioningMode;
|
|
77
94
|
}
|
|
78
95
|
|
|
96
|
+
/**
|
|
97
|
+
* A lifecycle-managed provider request that addresses the storage account
|
|
98
|
+
* rather than one bucket reference, such as a bucket lookup or creation.
|
|
99
|
+
*/
|
|
100
|
+
export interface IAccountOperation extends Omit<IBasicBucketOperation, 'bucketName'> {}
|
|
101
|
+
|
|
79
102
|
const lifecycleRecords = new WeakMap<
|
|
80
103
|
ISmartBucketCapabilityRef,
|
|
81
104
|
ICapabilityLifecycleRecord
|
|
@@ -102,14 +125,15 @@ const abortAndDisposeIndependentWork = (
|
|
|
102
125
|
activeOperationArg.ownedIndependentAborts.clear();
|
|
103
126
|
};
|
|
104
127
|
|
|
128
|
+
const bucketDetachedMessage = 'The Bucket is not attached to a lifecycle-managed SmartBucket.';
|
|
129
|
+
|
|
105
130
|
const getLifecycleRecord = (
|
|
106
131
|
smartbucketRefArg: ISmartBucketCapabilityRef,
|
|
132
|
+
detachedMessageArg = bucketDetachedMessage,
|
|
107
133
|
): ICapabilityLifecycleRecord => {
|
|
108
134
|
const lifecycleRecord = lifecycleRecords.get(smartbucketRefArg);
|
|
109
135
|
if (!lifecycleRecord) {
|
|
110
|
-
throw new Error(
|
|
111
|
-
'The Bucket is not attached to a lifecycle-managed SmartBucket.',
|
|
112
|
-
);
|
|
136
|
+
throw new Error(detachedMessageArg);
|
|
113
137
|
}
|
|
114
138
|
return lifecycleRecord;
|
|
115
139
|
};
|
|
@@ -189,11 +213,11 @@ export const beginOperationLifecycleClose = (
|
|
|
189
213
|
}
|
|
190
214
|
lifecycleRecord.state = 'closing';
|
|
191
215
|
lifecycleRecord.generation++;
|
|
192
|
-
const
|
|
193
|
-
'The SmartBucket lifecycle closed during an object operation.',
|
|
194
|
-
);
|
|
216
|
+
const lifecycleAbortCauses = new Map<IActiveCapabilityOperation, Error>();
|
|
195
217
|
const activeSettlements: Promise<void>[] = [];
|
|
196
218
|
for (const activeOperation of lifecycleRecord.activeOperations) {
|
|
219
|
+
const lifecycleAbortCause = new Error(activeOperation.closedDuringMessage);
|
|
220
|
+
lifecycleAbortCauses.set(activeOperation, lifecycleAbortCause);
|
|
197
221
|
activeSettlements.push(activeOperation.settlement);
|
|
198
222
|
activeOperation.abortController.abort(lifecycleAbortCause);
|
|
199
223
|
}
|
|
@@ -206,7 +230,10 @@ export const beginOperationLifecycleClose = (
|
|
|
206
230
|
return new Promise<void>((resolveArg) => {
|
|
207
231
|
const settlementDeadline = setTimeout(() => {
|
|
208
232
|
for (const activeOperation of lifecycleRecord.activeOperations) {
|
|
209
|
-
activeOperation.forceFinish(
|
|
233
|
+
activeOperation.forceFinish(
|
|
234
|
+
lifecycleAbortCauses.get(activeOperation)
|
|
235
|
+
?? new Error(activeOperation.closedDuringMessage),
|
|
236
|
+
);
|
|
210
237
|
}
|
|
211
238
|
resolveArg();
|
|
212
239
|
}, operationCloseSettlementTimeoutMs);
|
|
@@ -230,28 +257,51 @@ export const completeOperationLifecycleClose = (
|
|
|
230
257
|
lifecycleRecords.delete(smartbucketRefArg);
|
|
231
258
|
};
|
|
232
259
|
|
|
260
|
+
const bucketIdentity = (bindingArg: IExactCapabilityBinding): IOperationIdentity => ({
|
|
261
|
+
isCurrent: () =>
|
|
262
|
+
bindingArg.bucketRef.smartbucketRef === bindingArg.smartbucketRef &&
|
|
263
|
+
bindingArg.bucketRef.name === bindingArg.bucketName,
|
|
264
|
+
staleMessage: 'The exact upload capability is invalid or stale.',
|
|
265
|
+
detachedMessage: bucketDetachedMessage,
|
|
266
|
+
closedDuringMessage: 'The SmartBucket lifecycle closed during an object operation.',
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
const accountIdentity: IOperationIdentity = {
|
|
270
|
+
isCurrent: () => true,
|
|
271
|
+
staleMessage: 'The bucket request is stale.',
|
|
272
|
+
detachedMessage: 'The SmartBucket is closed; the bucket request was not sent.',
|
|
273
|
+
closedDuringMessage: 'The SmartBucket lifecycle closed during a bucket request.',
|
|
274
|
+
};
|
|
275
|
+
|
|
233
276
|
const assertBindingCurrent = (
|
|
234
|
-
bindingArg:
|
|
277
|
+
bindingArg: ILifecycleBinding,
|
|
278
|
+
identityArg: IOperationIdentity,
|
|
235
279
|
): void => {
|
|
236
|
-
const lifecycleRecord = getLifecycleRecord(
|
|
280
|
+
const lifecycleRecord = getLifecycleRecord(
|
|
281
|
+
bindingArg.smartbucketRef,
|
|
282
|
+
identityArg.detachedMessage,
|
|
283
|
+
);
|
|
237
284
|
assertOpenLifecycle(lifecycleRecord);
|
|
238
285
|
if (
|
|
239
|
-
|
|
240
|
-
bindingArg.bucketRef.name !== bindingArg.bucketName ||
|
|
286
|
+
!identityArg.isCurrent() ||
|
|
241
287
|
lifecycleRecord.generation !== bindingArg.lifecycleGeneration ||
|
|
242
288
|
lifecycleRecord.storageClient !== bindingArg.storageClient ||
|
|
243
289
|
bindingArg.smartbucketRef.storageClient !== bindingArg.storageClient
|
|
244
290
|
) {
|
|
245
|
-
throw new Error(
|
|
291
|
+
throw new Error(identityArg.staleMessage);
|
|
246
292
|
}
|
|
247
293
|
};
|
|
248
294
|
|
|
249
|
-
const
|
|
250
|
-
bindingArg:
|
|
295
|
+
const beginLifecycleOperation = (
|
|
296
|
+
bindingArg: ILifecycleBinding,
|
|
297
|
+
identityArg: IOperationIdentity,
|
|
251
298
|
callerSignalArg?: AbortSignal,
|
|
252
|
-
):
|
|
253
|
-
assertBindingCurrent(bindingArg);
|
|
254
|
-
const lifecycleRecord = getLifecycleRecord(
|
|
299
|
+
): IAccountOperation => {
|
|
300
|
+
assertBindingCurrent(bindingArg, identityArg);
|
|
301
|
+
const lifecycleRecord = getLifecycleRecord(
|
|
302
|
+
bindingArg.smartbucketRef,
|
|
303
|
+
identityArg.detachedMessage,
|
|
304
|
+
);
|
|
255
305
|
const operationAbortController = new AbortController();
|
|
256
306
|
let settleOperation!: () => void;
|
|
257
307
|
const operationSettlement = new Promise<void>((resolveArg) => {
|
|
@@ -284,6 +334,7 @@ const beginOperation = (
|
|
|
284
334
|
};
|
|
285
335
|
const activeOperation: IActiveCapabilityOperation = {
|
|
286
336
|
abortController: operationAbortController,
|
|
337
|
+
closedDuringMessage: identityArg.closedDuringMessage,
|
|
287
338
|
ownedIndependentAborts: new Set(),
|
|
288
339
|
settlement: operationSettlement,
|
|
289
340
|
settle: settleOperation,
|
|
@@ -306,7 +357,6 @@ const beginOperation = (
|
|
|
306
357
|
}
|
|
307
358
|
|
|
308
359
|
return {
|
|
309
|
-
bucketName: bindingArg.bucketName,
|
|
310
360
|
client: bindingArg.storageClient,
|
|
311
361
|
signal: operationAbortController.signal,
|
|
312
362
|
registerOwnedIndependentAbort(
|
|
@@ -332,8 +382,7 @@ const beginOperation = (
|
|
|
332
382
|
if (
|
|
333
383
|
lifecycleRecord.state === 'closed' ||
|
|
334
384
|
!lifecycleRecord.activeOperations.has(activeOperation) ||
|
|
335
|
-
|
|
336
|
-
bindingArg.bucketRef.name !== bindingArg.bucketName ||
|
|
385
|
+
!identityArg.isCurrent() ||
|
|
337
386
|
lifecycleRecord.storageClient !== bindingArg.storageClient ||
|
|
338
387
|
bindingArg.smartbucketRef.storageClient !== bindingArg.storageClient
|
|
339
388
|
) {
|
|
@@ -346,7 +395,7 @@ const beginOperation = (
|
|
|
346
395
|
if (operationAbortController.signal.aborted) {
|
|
347
396
|
throw operationAbortController.signal.reason;
|
|
348
397
|
}
|
|
349
|
-
assertBindingCurrent(bindingArg);
|
|
398
|
+
assertBindingCurrent(bindingArg, identityArg);
|
|
350
399
|
},
|
|
351
400
|
runProvider<T>(
|
|
352
401
|
sendArg: () => Promise<T>,
|
|
@@ -456,6 +505,38 @@ const beginOperation = (
|
|
|
456
505
|
};
|
|
457
506
|
};
|
|
458
507
|
|
|
508
|
+
const beginOperation = (
|
|
509
|
+
bindingArg: IExactCapabilityBinding,
|
|
510
|
+
callerSignalArg?: AbortSignal,
|
|
511
|
+
): IBasicBucketOperation => Object.assign(
|
|
512
|
+
beginLifecycleOperation(bindingArg, bucketIdentity(bindingArg), callerSignalArg),
|
|
513
|
+
{ bucketName: bindingArg.bucketName },
|
|
514
|
+
);
|
|
515
|
+
|
|
516
|
+
/**
|
|
517
|
+
* Begins an account-scoped request under the SmartBucket lifecycle: the
|
|
518
|
+
* caller's signal and `SmartBucket.close()` both abort it, close waits for it,
|
|
519
|
+
* and the storage client cannot change while it is active.
|
|
520
|
+
*/
|
|
521
|
+
export const beginAccountOperation = (
|
|
522
|
+
smartbucketRefArg: ISmartBucketCapabilityRef,
|
|
523
|
+
callerSignalArg?: AbortSignal,
|
|
524
|
+
): IAccountOperation => {
|
|
525
|
+
const lifecycleRecord = getLifecycleRecord(
|
|
526
|
+
smartbucketRefArg,
|
|
527
|
+
accountIdentity.detachedMessage,
|
|
528
|
+
);
|
|
529
|
+
return beginLifecycleOperation(
|
|
530
|
+
{
|
|
531
|
+
smartbucketRef: smartbucketRefArg,
|
|
532
|
+
storageClient: lifecycleRecord.storageClient,
|
|
533
|
+
lifecycleGeneration: lifecycleRecord.generation,
|
|
534
|
+
},
|
|
535
|
+
accountIdentity,
|
|
536
|
+
callerSignalArg,
|
|
537
|
+
);
|
|
538
|
+
};
|
|
539
|
+
|
|
459
540
|
export const beginBasicBucketOperation = (
|
|
460
541
|
bucketRefArg: IBucketCapabilityRef,
|
|
461
542
|
callerSignalArg?: AbortSignal,
|