@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.
Files changed (51) hide show
  1. package/dist_ts/00_commitinfo_data.js +1 -1
  2. package/dist_ts/nosqldb/classes.atomicdelete.d.ts +1 -0
  3. package/dist_ts/nosqldb/classes.atomicdelete.js +10 -7
  4. package/dist_ts/nosqldb/classes.atomicfindoneandupdate.d.ts +2 -0
  5. package/dist_ts/nosqldb/classes.atomicfindoneandupdate.js +9 -5
  6. package/dist_ts/nosqldb/classes.atomicupdate.d.ts +2 -0
  7. package/dist_ts/nosqldb/classes.atomicupdate.js +13 -8
  8. package/dist_ts/nosqldb/classes.collection.d.ts +13 -4
  9. package/dist_ts/nosqldb/classes.collection.js +57 -64
  10. package/dist_ts/nosqldb/classes.db.d.ts +1 -1
  11. package/dist_ts/nosqldb/classes.doc.d.ts +60 -3
  12. package/dist_ts/nosqldb/classes.doc.js +68 -26
  13. package/dist_ts/nosqldb/classes.exactpersistence.d.ts +36 -7
  14. package/dist_ts/nosqldb/classes.exactpersistence.js +92 -81
  15. package/dist_ts/nosqldb/classes.operationdeadline.d.ts +61 -0
  16. package/dist_ts/nosqldb/classes.operationdeadline.js +149 -0
  17. package/dist_ts/nosqldb/classes.persistence.d.ts +16 -1
  18. package/dist_ts/nosqldb/classes.persistence.js +20 -1
  19. package/dist_ts/nosqldb/classes.session.d.ts +73 -2
  20. package/dist_ts/nosqldb/classes.session.js +164 -38
  21. package/dist_ts/objectstorage/classes.bucket.d.ts +61 -3
  22. package/dist_ts/objectstorage/classes.bucket.js +243 -184
  23. package/dist_ts/objectstorage/classes.directory.js +17 -28
  24. package/dist_ts/objectstorage/classes.smartbucket.d.ts +13 -6
  25. package/dist_ts/objectstorage/classes.smartbucket.js +18 -12
  26. package/dist_ts/objectstorage/classes.watcher.js +1 -2
  27. package/dist_ts/objectstorage/interfaces.d.ts +8 -0
  28. package/dist_ts/objectstorage/internal.bucketrequests.d.ts +14 -0
  29. package/dist_ts/objectstorage/internal.bucketrequests.js +53 -0
  30. package/dist_ts/objectstorage/internal.exactupload.capability.d.ts +12 -0
  31. package/dist_ts/objectstorage/internal.exactupload.capability.js +46 -17
  32. package/package.json +3 -3
  33. package/readme.md +13 -2
  34. package/ts/00_commitinfo_data.ts +1 -1
  35. package/ts/nosqldb/classes.atomicdelete.ts +10 -6
  36. package/ts/nosqldb/classes.atomicfindoneandupdate.ts +10 -4
  37. package/ts/nosqldb/classes.atomicupdate.ts +14 -7
  38. package/ts/nosqldb/classes.collection.ts +82 -73
  39. package/ts/nosqldb/classes.db.ts +1 -1
  40. package/ts/nosqldb/classes.doc.ts +162 -33
  41. package/ts/nosqldb/classes.exactpersistence.ts +131 -93
  42. package/ts/nosqldb/classes.operationdeadline.ts +179 -0
  43. package/ts/nosqldb/classes.persistence.ts +36 -1
  44. package/ts/nosqldb/classes.session.ts +221 -39
  45. package/ts/objectstorage/classes.bucket.ts +350 -218
  46. package/ts/objectstorage/classes.directory.ts +18 -27
  47. package/ts/objectstorage/classes.smartbucket.ts +21 -11
  48. package/ts/objectstorage/classes.watcher.ts +0 -1
  49. package/ts/objectstorage/interfaces.ts +9 -0
  50. package/ts/objectstorage/internal.bucketrequests.ts +70 -0
  51. 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
- try {
183
- const { commonPrefixes } = await this.listObjectsV2AllPages(this.getBasePath(), '/');
184
- const directoryArray: Directory[] = [];
185
-
186
- if (commonPrefixes) {
187
- commonPrefixes.forEach((item) => {
188
- if (item.Prefix) {
189
- const subtractedPath = item.Prefix.replace(this.getBasePath(), '');
190
- if (subtractedPath.endsWith('/')) {
191
- const dirName = subtractedPath.slice(0, -1);
192
- // Ensure the directory name is not empty (which would indicate the base directory itself)
193
- if (dirName) {
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
- if (childDirectories.length === 0) {
413
- console.log('Directory empty! Path complete!');
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
- public async getBucketByName(bucketNameArg: string) {
120
- return Bucket.getBucketByName(this, bucketNameArg);
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
- * Check if a bucket exists
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(bucketNameArg: string): Promise<boolean> {
127
- const command = new plugins.s3.ListBucketsCommand({});
128
- const buckets = await this.currentStorageClient.send(command);
129
- return buckets.Buckets?.some(bucket => bucket.Name === bucketNameArg) ?? false;
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
  /**
@@ -83,7 +83,6 @@ export class BucketWatcher extends EventEmitter {
83
83
  */
84
84
  public async start(): Promise<void> {
85
85
  if (this.pollIntervalId !== null) {
86
- console.log('BucketWatcher is already running');
87
86
  return;
88
87
  }
89
88
 
@@ -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 IExactCapabilityBinding {
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 lifecycleAbortCause = new Error(
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(lifecycleAbortCause);
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: IExactCapabilityBinding,
277
+ bindingArg: ILifecycleBinding,
278
+ identityArg: IOperationIdentity,
235
279
  ): void => {
236
- const lifecycleRecord = getLifecycleRecord(bindingArg.smartbucketRef);
280
+ const lifecycleRecord = getLifecycleRecord(
281
+ bindingArg.smartbucketRef,
282
+ identityArg.detachedMessage,
283
+ );
237
284
  assertOpenLifecycle(lifecycleRecord);
238
285
  if (
239
- bindingArg.bucketRef.smartbucketRef !== bindingArg.smartbucketRef ||
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('The exact upload capability is invalid or stale.');
291
+ throw new Error(identityArg.staleMessage);
246
292
  }
247
293
  };
248
294
 
249
- const beginOperation = (
250
- bindingArg: IExactCapabilityBinding,
295
+ const beginLifecycleOperation = (
296
+ bindingArg: ILifecycleBinding,
297
+ identityArg: IOperationIdentity,
251
298
  callerSignalArg?: AbortSignal,
252
- ): IBasicBucketOperation => {
253
- assertBindingCurrent(bindingArg);
254
- const lifecycleRecord = getLifecycleRecord(bindingArg.smartbucketRef);
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
- bindingArg.bucketRef.smartbucketRef !== bindingArg.smartbucketRef ||
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,