@lossless.org/client 1.7.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 +8 -3
  22. package/dist_ts/objectstorage/classes.bucket.js +83 -131
  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 +1 -1
  33. package/readme.md +6 -1
  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 +111 -158
  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
@@ -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,