@cloudbitmaps/core 0.21.0 → 0.23.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 (98) hide show
  1. package/README.md +23 -8
  2. package/dist/chunk-7GT632UF.js +641 -0
  3. package/dist/chunk-7GT632UF.js.map +7 -0
  4. package/dist/chunk-CGILDXGP.js +73 -0
  5. package/dist/chunk-CGILDXGP.js.map +7 -0
  6. package/dist/chunk-GKHERVVQ.js +121 -0
  7. package/dist/chunk-GKHERVVQ.js.map +7 -0
  8. package/dist/{chunk-IGLIZBR7.js → chunk-MVUD4R72.js} +253 -765
  9. package/dist/chunk-MVUD4R72.js.map +7 -0
  10. package/dist/core/audit.d.ts +53 -15
  11. package/dist/core/bit-route.d.ts +9 -0
  12. package/dist/core/blob.d.ts +4 -0
  13. package/dist/core/budget.d.ts +8 -3
  14. package/dist/core/check.d.ts +27 -0
  15. package/dist/core/codec.d.ts +1 -1
  16. package/dist/core/combine-expr.d.ts +9 -5
  17. package/dist/core/combine-feed.d.ts +11 -3
  18. package/dist/core/combine-many.d.ts +28 -13
  19. package/dist/core/consistency.d.ts +11 -2
  20. package/dist/core/cooperative.d.ts +7 -0
  21. package/dist/core/crbm-storage-source.d.ts +7 -4
  22. package/dist/core/crypto.d.ts +5 -0
  23. package/dist/core/determinism.d.ts +2 -1
  24. package/dist/core/discover.d.ts +5 -3
  25. package/dist/core/engine.d.ts +4 -4
  26. package/dist/core/erase-id.d.ts +52 -19
  27. package/dist/core/erasure.d.ts +62 -28
  28. package/dist/core/errors.d.ts +83 -9
  29. package/dist/core/leases.d.ts +2 -0
  30. package/dist/core/load-input.d.ts +48 -0
  31. package/dist/core/load.d.ts +71 -16
  32. package/dist/core/metrics.d.ts +22 -1
  33. package/dist/core/pinned-storage-source.d.ts +27 -8
  34. package/dist/core/pointer-id.d.ts +9 -0
  35. package/dist/core/ports.d.ts +107 -13
  36. package/dist/core/preflight.d.ts +80 -0
  37. package/dist/core/registry-scan.d.ts +0 -5
  38. package/dist/core/retention-sweep.d.ts +61 -16
  39. package/dist/core/retention.d.ts +18 -7
  40. package/dist/core/retry.d.ts +5 -2
  41. package/dist/core/rollback.d.ts +23 -12
  42. package/dist/core/validate.d.ts +33 -2
  43. package/dist/driver-kit.d.ts +9 -4
  44. package/dist/driver-kit.js +63 -27
  45. package/dist/driver-kit.js.map +2 -2
  46. package/dist/drivers/_shared/object-registry.d.ts +24 -1
  47. package/dist/drivers/_shared/options.d.ts +15 -7
  48. package/dist/drivers/backends.d.ts +19 -2
  49. package/dist/drivers/crypto.d.ts +18 -3
  50. package/dist/export/index.d.ts +25 -10
  51. package/dist/index.d.ts +19 -17
  52. package/dist/index.js +2457 -2283
  53. package/dist/index.js.map +4 -4
  54. package/dist/node.d.ts +14 -0
  55. package/dist/node.js +1064 -0
  56. package/dist/node.js.map +7 -0
  57. package/package.json +16 -2
  58. package/dist/chunk-IGLIZBR7.js.map +0 -7
  59. package/dist/core/absent-operands.d.ts +0 -8
  60. package/dist/core/bulk-load.d.ts +0 -148
  61. package/dist/core/cas-retry.d.ts +0 -27
  62. package/dist/core/chunk-checks.d.ts +0 -39
  63. package/dist/core/chunk-stream.d.ts +0 -22
  64. package/dist/core/chunk-window.d.ts +0 -37
  65. package/dist/core/combine-ledger.d.ts +0 -43
  66. package/dist/core/crbm/crc32c.d.ts +0 -10
  67. package/dist/core/crbm/fingerprint.d.ts +0 -23
  68. package/dist/core/crbm/format.d.ts +0 -150
  69. package/dist/core/crbm/frame.d.ts +0 -10
  70. package/dist/core/crbm/plan-reads.d.ts +0 -50
  71. package/dist/core/crbm/varint.d.ts +0 -14
  72. package/dist/core/crbm/writer.d.ts +0 -72
  73. package/dist/core/crbm-open.d.ts +0 -154
  74. package/dist/core/crbm-write.d.ts +0 -75
  75. package/dist/core/due-index.d.ts +0 -53
  76. package/dist/core/generation-gc.d.ts +0 -293
  77. package/dist/core/generation-list.d.ts +0 -11
  78. package/dist/core/id-window.d.ts +0 -46
  79. package/dist/core/invalidation-scope.d.ts +0 -77
  80. package/dist/core/item-pull.d.ts +0 -29
  81. package/dist/core/known-generations-shape.d.ts +0 -13
  82. package/dist/core/known-generations.d.ts +0 -147
  83. package/dist/core/ledger-seed.d.ts +0 -17
  84. package/dist/core/metadata.d.ts +0 -42
  85. package/dist/core/order.d.ts +0 -2
  86. package/dist/core/publish.d.ts +0 -207
  87. package/dist/core/reader-defaults.d.ts +0 -29
  88. package/dist/core/summary.d.ts +0 -110
  89. package/dist/core/token.d.ts +0 -41
  90. package/dist/drivers/_shared/entropy.d.ts +0 -5
  91. package/dist/drivers/_shared/registry.d.ts +0 -107
  92. package/dist/drivers/localfs/exact-case.d.ts +0 -34
  93. package/dist/drivers/localfs/fs-util.d.ts +0 -55
  94. package/dist/drivers/localfs/paths.d.ts +0 -40
  95. package/dist/drivers/localfs/registry.d.ts +0 -50
  96. package/dist/drivers/localfs/storage.d.ts +0 -28
  97. package/dist/drivers/memory.d.ts +0 -64
  98. package/dist/testing/fuzz-core.d.ts +0 -15
@@ -3,8 +3,9 @@ var ERROR_BRAND = /* @__PURE__ */ Symbol.for("cloudbitmaps.error");
3
3
  var TRANSIENT_BRAND = /* @__PURE__ */ Symbol.for("cloudbitmaps.error.transient");
4
4
  var NAMES = /* @__PURE__ */ new WeakMap();
5
5
  var CloudRoaringError = class extends Error {
6
- /** Cross-bundle brand — see the predicates ({@link isCloudRoaringError}, …). Non-enumerable-ish (symbol key ⇒ not in JSON). */
6
+ /** Cross-bundle brand — see {@link errorKind}. Non-enumerable-ish (symbol key ⇒ not in JSON). */
7
7
  [ERROR_BRAND] = true;
8
+ /** Builds the error from `message`, and sets `name` to the class's name as written. */
8
9
  constructor(message) {
9
10
  super(message);
10
11
  this.name = NAMES.get(new.target) ?? new.target.name;
@@ -12,6 +13,7 @@ var CloudRoaringError = class extends Error {
12
13
  };
13
14
  var ValidationError = class extends CloudRoaringError {
14
15
  };
16
+ var CONTENTION_NEXT_STEP = "; another writer changed the segment's row while this call ran. Re-run the call: it reads the row afresh";
15
17
  var WriteConflictError = class extends CloudRoaringError {
16
18
  };
17
19
  var IntegrityError = class extends CloudRoaringError {
@@ -23,6 +25,7 @@ var LeaseExpiredError = class extends CloudRoaringError {
23
25
  until;
24
26
  /** `'expired'` when the clock reached `until`, `'released'` after `release()`. */
25
27
  reason;
28
+ /** Builds the error from its `message`, the epoch-ms the lease ran until, and why it ended. */
26
29
  constructor(message, until, reason) {
27
30
  super(message);
28
31
  this.until = until;
@@ -32,11 +35,13 @@ var LeaseExpiredError = class extends CloudRoaringError {
32
35
  var LeaseLimitError = class extends CloudRoaringError {
33
36
  };
34
37
  var StaleOperandError = class extends CloudRoaringError {
38
+ /** The stable code `'stale-operand'`, to match on. */
35
39
  code = "stale-operand";
36
40
  /** The operand's name in the call that found it stale. */
37
41
  operand;
38
42
  /** Why it is stale. */
39
43
  reason;
44
+ /** Builds the error from its `message`, the operand's name and why it is stale. */
40
45
  constructor(message, operand, reason) {
41
46
  super(message);
42
47
  this.operand = operand;
@@ -48,12 +53,29 @@ var UnsupportedError = class extends CloudRoaringError {
48
53
  var CapabilityError = class extends CloudRoaringError {
49
54
  };
50
55
  var BudgetExceededError = class extends CloudRoaringError {
56
+ /** The option to raise to let the call through. */
57
+ option;
58
+ /** The bound in force: requests, bytes or rows, by `option`. */
59
+ limit;
60
+ /**
61
+ * What the call had reached when refused. By `option`: the requests the call needs (`'budget.maxRequests'`), a
62
+ * lower bound on the resident bytes it needs (`'maxBufferedBytes'`), or `limit + 1` (`'maxScanSegments'`).
63
+ */
64
+ spent;
65
+ /** Builds the error from its `message` and the `option`, `limit` and `spent` it reports. */
66
+ constructor(message, details) {
67
+ super(message);
68
+ this.option = details.option;
69
+ this.limit = details.limit;
70
+ this.spent = details.spent;
71
+ }
51
72
  };
52
73
  var KeyUnavailableError = class extends CloudRoaringError {
53
74
  };
54
75
  var TransientError = class extends CloudRoaringError {
55
76
  /** A second brand so a transient fault is classifiable cross-bundle. */
56
77
  [TRANSIENT_BRAND] = true;
78
+ /** Builds the error from `message`; `options.cause`, when given, is kept as `cause`. */
57
79
  constructor(message, options) {
58
80
  super(message);
59
81
  if (options && "cause" in options) this.cause = options.cause;
@@ -81,17 +103,17 @@ function isCloudRoaringError(err) {
81
103
  function isWriteConflictError(err) {
82
104
  return isCloudRoaringError(err) && err.name === "WriteConflictError";
83
105
  }
84
- function isTransientError(err) {
85
- return hasBrand(err, TRANSIENT_BRAND);
106
+ var UNSETTLED_BRAND = /* @__PURE__ */ Symbol.for("cloudbitmaps.error.unsettled-conflict");
107
+ function unsettledConflict(message) {
108
+ const err = new WriteConflictError(message);
109
+ Object.defineProperty(err, UNSETTLED_BRAND, { value: true });
110
+ return err;
86
111
  }
87
- function isLeaseExpiredError(err) {
88
- return isCloudRoaringError(err) && err.name === "LeaseExpiredError";
112
+ function isUnsettledConflict(err) {
113
+ return isWriteConflictError(err) && hasBrand(err, UNSETTLED_BRAND);
89
114
  }
90
- function isStaleOperandError(err) {
91
- return isCloudRoaringError(err) && err.name === "StaleOperandError";
92
- }
93
- function isLeaseLimitError(err) {
94
- return isCloudRoaringError(err) && err.name === "LeaseLimitError";
115
+ function isTransientError(err) {
116
+ return hasBrand(err, TRANSIENT_BRAND);
95
117
  }
96
118
  function isNotFoundError(err) {
97
119
  return isCloudRoaringError(err) && err.name === "NotFoundError";
@@ -102,32 +124,29 @@ function isIntegrityError(err) {
102
124
  function isValidationError(err) {
103
125
  return isCloudRoaringError(err) && err.name === "ValidationError";
104
126
  }
105
- function isUnsupportedError(err) {
106
- return isCloudRoaringError(err) && err.name === "UnsupportedError";
107
- }
108
- function isCapabilityError(err) {
109
- return isCloudRoaringError(err) && err.name === "CapabilityError";
110
- }
111
- function isBudgetExceededError(err) {
112
- return isCloudRoaringError(err) && err.name === "BudgetExceededError";
113
- }
114
127
  function isKeyUnavailableError(err) {
115
128
  return isCloudRoaringError(err) && err.name === "KeyUnavailableError";
116
129
  }
117
-
118
- // src/core/bit-route.ts
119
- var U32_MAX = 4294967295;
120
- var CHUNK_COUNT = 65536;
121
- var LOW_16 = 65535;
122
- function splitId(id) {
123
- if (!Number.isInteger(id) || id < 0 || id > U32_MAX) {
124
- throw new ValidationError(`an id must be an integer from 0 to ${U32_MAX}`);
125
- }
126
- return { chunkKey: id >>> 16, remainder: id & LOW_16 };
127
- }
128
- var MAX_REMAINDER = CHUNK_COUNT - 1;
129
- function joinId(chunkKey, remainder) {
130
- return ((chunkKey & LOW_16) << 16 >>> 0) + (remainder & LOW_16);
130
+ var KIND_NAMES = {
131
+ CloudRoaringError: true,
132
+ ValidationError: true,
133
+ WriteConflictError: true,
134
+ IntegrityError: true,
135
+ NotFoundError: true,
136
+ UnsupportedError: true,
137
+ CapabilityError: true,
138
+ TransientError: true,
139
+ KeyUnavailableError: true,
140
+ BudgetExceededError: true,
141
+ LeaseExpiredError: true,
142
+ LeaseLimitError: true,
143
+ StaleOperandError: true
144
+ };
145
+ function errorKind(err) {
146
+ if (hasBrand(err, TRANSIENT_BRAND)) return "TransientError";
147
+ if (!hasBrand(err, ERROR_BRAND)) return void 0;
148
+ const name = err.name;
149
+ return typeof name === "string" && Object.hasOwn(KIND_NAMES, name) ? name : "CloudRoaringError";
131
150
  }
132
151
 
133
152
  // src/core/check.ts
@@ -153,6 +172,11 @@ function requireInteger(value, bounds) {
153
172
  }
154
173
  return value;
155
174
  }
175
+ function checkRequireEncryption(value) {
176
+ if (value !== void 0 && typeof value !== "boolean") {
177
+ throw new ValidationError(`requireEncryption must be a boolean; got ${shown(value)}`);
178
+ }
179
+ }
156
180
  function requirePositiveInteger(value, what) {
157
181
  return requireInteger(value, { min: 1, what });
158
182
  }
@@ -165,69 +189,71 @@ function requireFiniteNumber(value, bounds) {
165
189
  }
166
190
  return value;
167
191
  }
168
-
169
- // src/core/concurrency.ts
170
- async function mapWithConcurrency(items, limit, fn) {
171
- requirePositiveInteger(limit, "concurrency limit");
172
- const results = new Array(items.length);
173
- let next = 0;
174
- let failed = false;
175
- let firstError;
176
- const worker = async () => {
177
- while (!failed) {
178
- const i = next++;
179
- if (i >= items.length) return;
180
- try {
181
- results[i] = await fn(items[i], i);
182
- } catch (err) {
183
- if (!failed) {
184
- failed = true;
185
- firstError = err;
186
- }
187
- return;
188
- }
189
- }
190
- };
191
- const workers = Array.from({ length: Math.min(limit, items.length) }, () => worker());
192
- await Promise.all(workers);
193
- if (failed) throw firstError;
194
- return results;
192
+ function refuseUnknownOptions(name, options, keys, hint) {
193
+ if (options === null || typeof options !== "object") {
194
+ throw new ValidationError(
195
+ `${name} needs an options object \u2014 got ${options === null ? "null" : typeof options}`
196
+ );
197
+ }
198
+ const unknown = Object.keys(options).filter((k) => !keys.includes(k));
199
+ if (unknown.length > 0) {
200
+ const list = (ks) => ks.map((k) => `\`${k}\``).join(", ");
201
+ throw new ValidationError(
202
+ `${name} does not take ${list(unknown)}. It takes ${list(keys)}${hint === void 0 ? "" : `; ${hint}`}.`
203
+ );
204
+ }
195
205
  }
196
- function boundedAll(items, limit, fn) {
197
- try {
198
- requirePositiveInteger(limit, "concurrency limit");
199
- } catch (err) {
200
- return Promise.reject(err);
206
+ function requireBool(name, value) {
207
+ if (value === void 0) return void 0;
208
+ if (typeof value !== "boolean") {
209
+ throw new ValidationError(`${name} must be a boolean; got ${shown(value)}`);
201
210
  }
202
- if (items.length > limit) return inWaves(items, limit, fn);
203
- const calls = [];
204
- for (const item of items) {
205
- try {
206
- calls.push(fn(item));
207
- } catch (err) {
208
- void Promise.allSettled(calls);
209
- return Promise.reject(err);
210
- }
211
+ return value;
212
+ }
213
+ function requireNonEmptyString(name, value) {
214
+ if (value === void 0) return void 0;
215
+ if (typeof value !== "string" || value.trim().length === 0) {
216
+ throw new ValidationError(
217
+ `${name} must be a non-empty string; got ${typeof value === "string" ? "an empty string" : shown(value)}`
218
+ );
211
219
  }
212
- return Promise.all(calls);
220
+ return value;
213
221
  }
214
- async function inWaves(items, limit, fn) {
215
- const results = new Array(items.length);
216
- let next = 0;
217
- let failed = false;
218
- const worker = async () => {
219
- while (!failed && next < items.length) {
220
- const i = next++;
221
- try {
222
- results[i] = await fn(items[i]);
223
- } catch (err) {
224
- failed = true;
225
- throw err;
226
- }
227
- }
228
- };
229
- await Promise.all(Array.from({ length: limit }, () => worker()));
230
- return results;
222
+
223
+ // src/core/bit-route.ts
224
+ var U32_MAX = 4294967295;
225
+ var CHUNK_COUNT = 65536;
226
+ var LOW_16 = 65535;
227
+ var idErrors = /* @__PURE__ */ new WeakSet();
228
+ function isBadIdError(err) {
229
+ return err instanceof ValidationError && idErrors.has(err);
230
+ }
231
+ function badId(id) {
232
+ let got;
233
+ if (id === null) got = "null";
234
+ else if (id === void 0) got = "undefined";
235
+ else if (typeof id === "object") got = "an object";
236
+ else if (typeof id !== "number") got = `${/^[aeiou]/.test(typeof id) ? "an" : "a"} ${typeof id}`;
237
+ else if (!Number.isFinite(id)) got = "NaN or an infinity";
238
+ else if (id < 0) got = "a negative number";
239
+ else if (!Number.isInteger(id)) got = "a number with a fraction";
240
+ else got = `a number above ${U32_MAX} (ids are 32-bit)`;
241
+ const err = new ValidationError(`an id must be an integer from 0 to ${U32_MAX}; got ${got}`);
242
+ idErrors.add(err);
243
+ return err;
244
+ }
245
+ function refusalOfId(id) {
246
+ return badId(id);
247
+ }
248
+ function splitId(id) {
249
+ if (!Number.isInteger(id) || id < 0 || id > U32_MAX) {
250
+ throw badId(id);
251
+ }
252
+ return { chunkKey: id >>> 16, remainder: id & LOW_16 };
253
+ }
254
+ var MAX_REMAINDER = CHUNK_COUNT - 1;
255
+ function joinId(chunkKey, remainder) {
256
+ return ((chunkKey & LOW_16) << 16 >>> 0) + (remainder & LOW_16);
231
257
  }
232
258
 
233
259
  // src/core/name-codec.ts
@@ -322,7 +348,15 @@ function validatePart(value, field) {
322
348
  );
323
349
  }
324
350
  }
351
+ var REF_KEYS = ["segment", "namespace", "currentGen", "status"];
352
+ function requireRefObject(ref, hint = "") {
353
+ if (typeof ref !== "object" || ref === null) {
354
+ throw new ValidationError(`a segment ref is an object such as { segment: 'name' }${hint}`);
355
+ }
356
+ }
325
357
  function validateSegmentRef(ref) {
358
+ requireRefObject(ref);
359
+ refuseUnknownKeys(ref);
326
360
  validatePart(ref.segment, "segment");
327
361
  if (ref.namespace !== void 0) validatePart(ref.namespace, "namespace");
328
362
  }
@@ -338,7 +372,56 @@ function validateUserNamespace(namespace) {
338
372
  );
339
373
  }
340
374
  }
341
- function validateUserRef(ref) {
375
+ var PORT_KEYS = [
376
+ "generation",
377
+ "chunkKey",
378
+ "wrappedDeks",
379
+ "keyId",
380
+ "retention",
381
+ "residency",
382
+ "summary",
383
+ "knownGens",
384
+ "leases",
385
+ "createdAt",
386
+ "updatedAt",
387
+ "token",
388
+ "pointerId"
389
+ ];
390
+ var SEGMENT_HANDLE_MARK = /* @__PURE__ */ Symbol.for("cloudbitmaps.roaring.segment/1");
391
+ function unknownKeysOf(ref, extraKeys) {
392
+ return Object.keys(ref).filter(
393
+ (k) => !REF_KEYS.includes(k) && !extraKeys.includes(k)
394
+ );
395
+ }
396
+ function refuseUnknownKeys(ref) {
397
+ const unknown = unknownKeysOf(ref, PORT_KEYS);
398
+ if (unknown.length === 0) return;
399
+ const list = unknown.map((k) => `"${k}"`).join(", ");
400
+ throw new ValidationError(
401
+ `a driver port: unknown ${unknown.length === 1 ? "key" : "keys"} ${list} in the segment ref; it takes { segment, namespace } and a generation key's \`generation\`, a chunk ref's \`chunkKey\`, a \`segments()\` row's \`currentGen\` and \`status\`, or a registry row's own fields (a segment addressed without its namespace is a different segment)`
402
+ );
403
+ }
404
+ function refuseUnknownRefKeys(ref, verb, extraKeys = []) {
405
+ if (ref[SEGMENT_HANDLE_MARK] === true) {
406
+ throw new ValidationError(
407
+ `${verb}: a segment handle is not a ref; pass { segment, namespace }`
408
+ );
409
+ }
410
+ if (Array.isArray(ref)) {
411
+ throw new ValidationError(
412
+ `${verb}: a segment ref is an object such as { segment: 'name' }, not an array`
413
+ );
414
+ }
415
+ const unknown = unknownKeysOf(ref, extraKeys);
416
+ if (unknown.length === 0) return;
417
+ const list = unknown.map((k) => `"${k}"`).join(", ");
418
+ throw new ValidationError(
419
+ `${verb}: unknown ${unknown.length === 1 ? "key" : "keys"} ${list} in the segment ref; a ref takes { segment, namespace } (and a \`segments()\` row's \`currentGen\` and \`status\`, which are read for nothing); a segment addressed without its namespace is a different segment`
420
+ );
421
+ }
422
+ function validateUserRef(ref, verb, extraKeys = []) {
423
+ requireRefObject(ref, "; store.segment() takes the bare name");
424
+ if (verb !== void 0) refuseUnknownRefKeys(ref, verb, extraKeys);
342
425
  validatePart(ref.segment, "segment");
343
426
  if (ref.namespace !== void 0) validateUserNamespace(ref.namespace);
344
427
  }
@@ -401,6 +484,22 @@ function isStorageBackend(value) {
401
484
  return typeof value === "object" && value !== null && value[STORAGE_BACKEND_BRAND] === true;
402
485
  }
403
486
 
487
+ // src/core/token.ts
488
+ var INCARNATION_TOKEN = /^([0-9a-f]{32})\.(0|[1-9]\d*)\.([0-9a-f]{16})$/;
489
+ function incarnationOf(token) {
490
+ return INCARNATION_TOKEN.exec(token)?.[1];
491
+ }
492
+ function incarnationField(token) {
493
+ const incarnation = token === void 0 ? void 0 : incarnationOf(token);
494
+ return incarnation === void 0 ? {} : { incarnation };
495
+ }
496
+ function sameIncarnation(a, b) {
497
+ const ia = incarnationOf(a.token);
498
+ const ib = incarnationOf(b.token);
499
+ if (ia !== void 0 || ib !== void 0) return ia === ib;
500
+ return a.createdAt === b.createdAt;
501
+ }
502
+
404
503
  // src/core/pointer-id.ts
405
504
  var RESOLVED_FIELDS = [
406
505
  "currentGen",
@@ -424,6 +523,11 @@ function pointerIdOf(row) {
424
523
  }
425
524
  return pointerId;
426
525
  }
526
+ function pointerSetBy(now, before, generation, sent) {
527
+ if (now === null || now.status === "destroyed" || now.currentGen !== generation) return false;
528
+ if (before !== null && !sameIncarnation(before, now)) return false;
529
+ return sent.has(now.pointerId);
530
+ }
427
531
 
428
532
  // src/core/crypto.ts
429
533
  var UTF82 = new TextEncoder();
@@ -516,22 +620,6 @@ async function withRetry(op, policy, deps) {
516
620
  throw lastErr;
517
621
  }
518
622
 
519
- // src/core/token.ts
520
- var INCARNATION_TOKEN = /^([0-9a-f]{32})\.(0|[1-9]\d*)\.([0-9a-f]{16})$/;
521
- function incarnationOf(token) {
522
- return INCARNATION_TOKEN.exec(token)?.[1];
523
- }
524
- function incarnationField(token) {
525
- const incarnation = token === void 0 ? void 0 : incarnationOf(token);
526
- return incarnation === void 0 ? {} : { incarnation };
527
- }
528
- function sameIncarnation(a, b) {
529
- const ia = incarnationOf(a.token);
530
- const ib = incarnationOf(b.token);
531
- if (ia !== void 0 || ib !== void 0) return ia === ib;
532
- return a.createdAt === b.createdAt;
533
- }
534
-
535
623
  // src/core/leases.ts
536
624
  var MAX_LEASE_MS = 14 * 864e5;
537
625
  var LEASE_SKEW_MS = 6e4;
@@ -657,7 +745,9 @@ async function takeLease(ref, deps, take) {
657
745
  }
658
746
  }
659
747
  if (unanswered !== void 0) throw unanswered;
660
- throw new WriteConflictError(`lease: contention taking a lease on segment "${ref.segment}"`);
748
+ throw new WriteConflictError(
749
+ `lease: contention taking a lease on segment "${ref.segment}"${CONTENTION_NEXT_STEP}`
750
+ );
661
751
  }
662
752
  async function releaseLease(ref, deps, holder) {
663
753
  let resends = 0;
@@ -691,54 +781,9 @@ async function releaseLease(ref, deps, holder) {
691
781
  }
692
782
  }
693
783
  if (unanswered !== void 0) throw unanswered;
694
- throw new WriteConflictError(`lease: contention releasing a lease on segment "${ref.segment}"`);
695
- }
696
-
697
- // src/drivers/_shared/keys.ts
698
- var DEFAULT_NAMESPACE = "_default";
699
- function namespaceKeyPart(namespace) {
700
- return namespace === void 0 ? DEFAULT_NAMESPACE : encodeNameForKey(namespace);
701
- }
702
- function namespacePathPart(namespace) {
703
- return namespace === void 0 ? DEFAULT_NAMESPACE : encodeNameForPath(namespace);
704
- }
705
-
706
- // src/drivers/_shared/options.ts
707
- function refuseUnknownOptions(name, options, keys, hint) {
708
- if (options === null || typeof options !== "object") {
709
- throw new ValidationError(
710
- `${name} needs an options object \u2014 got ${options === null ? "null" : typeof options}`
711
- );
712
- }
713
- const unknown = Object.keys(options).filter((k) => !keys.includes(k));
714
- if (unknown.length > 0) {
715
- const list = (ks) => ks.map((k) => `\`${k}\``).join(", ");
716
- throw new ValidationError(
717
- `${name} does not take ${list(unknown)}. It takes ${list(keys)}${hint === void 0 ? "" : `; ${hint}`}.`
718
- );
719
- }
720
- }
721
- function checkNow(name, now) {
722
- if (now !== void 0 && typeof now !== "function") {
723
- throw new ValidationError(`${name}: \`now\` must be a function returning epoch milliseconds`);
724
- }
725
- }
726
- function requirePositiveInt(name, value) {
727
- return value === void 0 ? void 0 : requirePositiveInteger(value, name);
728
- }
729
- function requireBool(name, value) {
730
- if (value === void 0) return void 0;
731
- if (typeof value !== "boolean") {
732
- throw new ValidationError(`${name} must be a boolean; got ${shown2(value)}`);
733
- }
734
- return value;
735
- }
736
- function shown2(value) {
737
- if (typeof value === "string") return JSON.stringify(value);
738
- if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") {
739
- return String(value);
740
- }
741
- return value === null ? "null" : typeof value;
784
+ throw new WriteConflictError(
785
+ `lease: contention releasing a lease on segment "${ref.segment}"${CONTENTION_NEXT_STEP}`
786
+ );
742
787
  }
743
788
 
744
789
  // src/core/crbm/frame.ts
@@ -1019,35 +1064,6 @@ function ownSummary(ref, generation, description, aead) {
1019
1064
  return summary;
1020
1065
  }
1021
1066
 
1022
- // src/core/known-generations-shape.ts
1023
- var ENTRY_KEYS = ["generation", "footerCrc"];
1024
- function knownGensShapeFault(value, max, show = String) {
1025
- if (value === void 0) return void 0;
1026
- if (!Array.isArray(value)) return "must be an array";
1027
- const list = value;
1028
- if (list.length > max) return `has ${list.length} entries, cap ${max}`;
1029
- const seen = /* @__PURE__ */ new Set();
1030
- for (const e of list) {
1031
- if (typeof e !== "object" || e === null || Array.isArray(e))
1032
- return "an entry must be an object";
1033
- const entry = e;
1034
- const extra = Object.keys(entry).filter((k) => !ENTRY_KEYS.includes(k));
1035
- if (extra.length > 0) {
1036
- return `an entry carries an undeclared field (${show(extra.join(", "))})`;
1037
- }
1038
- const { generation, footerCrc } = entry;
1039
- if (!Number.isSafeInteger(generation) || generation < 0) {
1040
- return `a generation must be a non-negative safe integer (got ${show(generation)})`;
1041
- }
1042
- if (!Number.isInteger(footerCrc) || footerCrc < 0 || footerCrc > 4294967295) {
1043
- return `a footerCrc must be an integer from 0 to 2^32 - 1 (got ${show(footerCrc)})`;
1044
- }
1045
- if (seen.has(generation)) return `generation ${String(generation)} is repeated`;
1046
- seen.add(generation);
1047
- }
1048
- return void 0;
1049
- }
1050
-
1051
1067
  // src/core/order.ts
1052
1068
  var ascending = (a, b) => a - b;
1053
1069
 
@@ -1200,558 +1216,39 @@ function leasedByRow(record) {
1200
1216
  return new Set((record.leases ?? []).map((e) => e.generation));
1201
1217
  }
1202
1218
 
1203
- // src/drivers/_shared/registry.ts
1204
- var STATUSES = ["active", "destroyed"];
1205
- var RECORD_FIELDS = [
1206
- "namespace",
1207
- "segment",
1208
- "currentGen",
1209
- "wrappedDeks",
1210
- "keyId",
1211
- "status",
1212
- "retention",
1213
- "residency",
1214
- "summary",
1215
- "createdAt",
1216
- "updatedAt",
1217
- "token",
1218
- "knownGens",
1219
- "leases",
1220
- "pointerId"
1221
- ];
1222
- var DRIVER_MANAGED = ["token", "pointerId"];
1223
- var ENVELOPE_FIELDS = ["schemaVersion", "deleted", "record"];
1224
- var MAX_GOVERNANCE_BYTES = 64 * 1024;
1225
- var MAX_WRAPPED_DEKS = 8;
1226
- var MAX_WRAPPED_DEK_BYTES = 4 * 1024;
1227
- var SEALED_SUMMARY_MIN_BYTES = 12 + 8 + 8 + 4 + 4 + 16;
1228
- var SEALED_SUMMARY_MAX_BYTES = SEALED_SUMMARY_MIN_BYTES + MAX_METADATA_BYTES;
1229
- var BASE642 = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/;
1230
- function validateGeneration(gen) {
1231
- if (gen === null) return;
1232
- if (!Number.isSafeInteger(gen) || gen < 0) {
1233
- throw new ValidationError(
1234
- `currentGen must be a non-negative safe integer or null (no Storage generation yet); got ${gen}`
1235
- );
1236
- }
1237
- }
1238
- function validatePatchGeneration(patch) {
1239
- if (!("currentGen" in patch)) return;
1240
- if (patch.currentGen === void 0) {
1241
- throw new ValidationError(
1242
- `currentGen was present in the patch but undefined. Pass \`null\` to clear the pointer (the segment has no Storage generation), or omit the key to leave it unchanged \u2014 an undefined value is refused because it would un-publish the segment's Storage data.`
1243
- );
1244
- }
1245
- validateGeneration(patch.currentGen);
1246
- }
1247
- function validateStatus(status) {
1248
- if (!STATUSES.includes(status)) {
1249
- throw new ValidationError(`status must be one of ${STATUSES.join("/")}; got ${status}`);
1250
- }
1251
- }
1252
- function validateGovernance(meta, field) {
1253
- if (meta === void 0) return;
1254
- if (meta === null || typeof meta !== "object" || Array.isArray(meta)) {
1255
- throw new ValidationError(
1256
- `${field} must be a plain object (got ${meta === null ? "null" : typeof meta})`
1257
- );
1258
- }
1259
- let json;
1260
- try {
1261
- json = JSON.stringify(meta);
1262
- } catch {
1263
- throw new ValidationError(`${field} must be JSON-serializable`);
1264
- }
1265
- if (json.length > MAX_GOVERNANCE_BYTES) {
1266
- throw new ValidationError(`${field} is ${json.length}B, exceeds cap ${MAX_GOVERNANCE_BYTES}B`);
1267
- }
1268
- }
1269
- function validateWrappedDeks(value, origin) {
1270
- if (value === void 0) return;
1271
- const fail = (msg) => {
1272
- throw origin === "stored" ? new IntegrityError(msg) : new ValidationError(msg);
1273
- };
1274
- if (!Array.isArray(value)) fail("wrappedDeks must be an array");
1275
- const list = value;
1276
- if (list.length === 0) fail("wrappedDeks must be non-empty when present");
1277
- if (list.length > MAX_WRAPPED_DEKS)
1278
- fail(`wrappedDeks has ${list.length} entries, cap ${MAX_WRAPPED_DEKS}`);
1279
- for (const w of list) {
1280
- if (w === null || typeof w !== "object") fail("wrappedDeks entry must be an object");
1281
- const { keyId, wrapped } = w;
1282
- if (typeof keyId !== "string" || keyId.length === 0)
1283
- fail("wrappedDeks entry needs a non-empty keyId");
1284
- if (typeof wrapped !== "string" || wrapped.length === 0)
1285
- fail("wrappedDeks entry needs a non-empty wrapped blob");
1286
- if (wrapped.length > MAX_WRAPPED_DEK_BYTES)
1287
- fail(`wrappedDeks entry is ${wrapped.length}B, cap ${MAX_WRAPPED_DEK_BYTES}B`);
1288
- }
1289
- }
1290
- var BASE64_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1291
- function base64PadBitsAreZero(text) {
1292
- const pad = text.endsWith("==") ? 2 : text.endsWith("=") ? 1 : 0;
1293
- if (pad === 0) return true;
1294
- const last = BASE64_ALPHABET.indexOf(text[text.length - pad - 1]);
1295
- return pad === 2 ? (last & 15) === 0 : (last & 3) === 0;
1296
- }
1297
- function validateSummary(value, origin, ctx) {
1298
- if (value === void 0) return void 0;
1299
- const fail = (msg) => {
1300
- const where = ctx === void 0 ? "" : `: ${ctx}`;
1301
- throw origin === "stored" ? new IntegrityError(`registry record summary: ${msg}${where}`) : new ValidationError(`summary: ${msg}${where}`);
1302
- };
1303
- if (value === null || typeof value !== "object" || Array.isArray(value)) {
1304
- fail(
1305
- `must be an object (got ${value === null ? "null" : Array.isArray(value) ? "an array" : typeof value})`
1306
- );
1307
- }
1308
- const s = value;
1309
- const keys = Object.keys(s);
1310
- const isSealed = keys.includes("sealed");
1311
- const allowed = isSealed ? ["generation", "sealed"] : ["generation", "cardinality", "fingerprint", "tailBytes", "metadata"];
1312
- const extra = keys.filter((k) => !allowed.includes(k));
1313
- if (extra.length > 0) {
1314
- fail(
1315
- `has fields its ${isSealed ? "sealed" : "clear"} shape does not declare (${extra.join(", ")})`
1316
- );
1317
- }
1318
- const generation = s.generation;
1319
- if (!Number.isSafeInteger(generation) || generation < 0) {
1320
- fail(`generation must be a non-negative safe integer (got ${String(generation)})`);
1321
- }
1322
- if (isSealed) {
1323
- const sealed = s.sealed;
1324
- if (typeof sealed !== "string" || !BASE642.test(sealed) || !base64PadBitsAreZero(sealed)) {
1325
- fail("sealed must be canonical, padded standard base64");
1326
- }
1327
- const text = sealed;
1328
- const bytes = text.length / 4 * 3 - (text.endsWith("==") ? 2 : text.endsWith("=") ? 1 : 0);
1329
- if (bytes < SEALED_SUMMARY_MIN_BYTES || bytes > SEALED_SUMMARY_MAX_BYTES) {
1330
- fail(
1331
- `sealed is ${bytes}B, outside ${SEALED_SUMMARY_MIN_BYTES}..${SEALED_SUMMARY_MAX_BYTES}B (a nonce, a fixed-width count, fingerprint and tail size, a tag, and metadata up to its cap)`
1332
- );
1333
- }
1334
- return Object.freeze({ generation, sealed: text });
1335
- }
1336
- const n = s.cardinality;
1337
- if (!Number.isInteger(n) || n < 0 || n > MAX_SUMMARY_CARDINALITY) {
1338
- fail(`cardinality must be an integer from 0 to 2^32 (got ${String(n)})`);
1339
- }
1340
- const fingerprint = s.fingerprint;
1341
- const parts = fingerprintParts(fingerprint);
1342
- if (parts === void 0) {
1343
- fail(
1344
- `fingerprint must be an object's, <size>:<footer checksum> in decimal (got ${shown3(JSON.stringify(fingerprint))})`
1345
- );
1346
- }
1347
- const tailBytes = s.tailBytes;
1348
- if (!tailBytesFit(tailBytes, parts.size)) {
1349
- fail(
1350
- `tailBytes must be an integer from the footer's size to the object's size less its preamble, and at most the cap of one open's tail read (got ${shown3(JSON.stringify(tailBytes))} for an object of ${parts.size} bytes)`
1351
- );
1352
- }
1353
- if (s.metadata === void 0) {
1354
- return Object.freeze({
1355
- generation,
1356
- cardinality: n,
1357
- fingerprint,
1358
- tailBytes
1359
- });
1360
- }
1361
- const canonical = canonicalMetadataJson(s.metadata, fail);
1362
- if (canonical === "{}") fail("metadata is empty; a generation without metadata carries none");
1363
- if (origin === "stored") return s;
1364
- const metadata = Object.freeze(JSON.parse(canonical));
1365
- return Object.freeze({
1366
- generation,
1367
- cardinality: n,
1368
- fingerprint,
1369
- tailBytes,
1370
- metadata
1371
- });
1372
- }
1373
- function validateSummaryNames(summary, currentGen) {
1374
- if (summary !== void 0 && summary.generation !== currentGen) {
1375
- throw new ValidationError(
1376
- `summary describes generation ${summary.generation}, but the row points at ${String(currentGen)}`
1377
- );
1378
- }
1379
- }
1380
- function summaryAgreesWithKeys(summary, wrappedDeks) {
1381
- return "sealed" in summary === (wrappedDeks !== void 0);
1382
- }
1383
- function validateSummaryKeys(summary, wrappedDeks) {
1384
- if (summary !== void 0 && !summaryAgreesWithKeys(summary, wrappedDeks)) {
1385
- throw new ValidationError(
1386
- wrappedDeks === void 0 ? "summary is sealed, but the row has no wrapped keys to open it with" : "summary is in the clear, but the row is encrypted: an encrypted segment's summary must be sealed"
1387
- );
1388
- }
1389
- }
1390
- function validateKnownGens(value, origin, ctx) {
1391
- if (value === void 0) return void 0;
1392
- const fail = (msg) => {
1393
- const where = ctx === void 0 ? "" : `: ${ctx}`;
1394
- throw origin === "stored" ? new IntegrityError(`registry record knownGens: ${msg}${where}`) : new ValidationError(`knownGens: ${msg}`);
1395
- };
1396
- const max = origin === "stored" ? MAX_STORED_KNOWN_GENERATIONS : MAX_KNOWN_GENERATIONS;
1397
- const fault = knownGensShapeFault(value, max, shown3);
1398
- if (fault !== void 0) fail(fault);
1399
- const out = [];
1400
- for (const entry of value) {
1401
- out.push(
1402
- origin === "stored" ? entry : Object.freeze({ generation: entry.generation, footerCrc: entry.footerCrc })
1403
- );
1404
- }
1405
- return origin === "stored" ? out : Object.freeze(out);
1406
- }
1407
- var HOLDER = /^[0-9a-f]{16}$/;
1408
- var LEASE_ENTRY_KEYS = ["holder", "generation", "until"];
1409
- function validateLeases(value, origin, ctx) {
1219
+ // src/core/known-generations-shape.ts
1220
+ var ENTRY_KEYS = ["generation", "footerCrc"];
1221
+ function knownGensShapeFault(value, max, show = String) {
1410
1222
  if (value === void 0) return void 0;
1411
- const fail = (msg) => {
1412
- const where = ctx === void 0 ? "" : `: ${ctx}`;
1413
- throw origin === "stored" ? new IntegrityError(`registry record leases: ${msg}${where}`) : new ValidationError(`leases: ${msg}`);
1414
- };
1415
- if (!Array.isArray(value)) fail("must be an array");
1223
+ if (!Array.isArray(value)) return "must be an array";
1416
1224
  const list = value;
1417
- const max = MAX_STORED_LEASES;
1418
- if (list.length > max) fail(`has ${list.length} entries, cap ${max}`);
1225
+ if (list.length > max) return `has ${list.length} entries, cap ${max}`;
1419
1226
  const seen = /* @__PURE__ */ new Set();
1420
- const out = [];
1421
1227
  for (const e of list) {
1422
- if (typeof e !== "object" || e === null || Array.isArray(e)) fail("an entry must be an object");
1228
+ if (typeof e !== "object" || e === null || Array.isArray(e))
1229
+ return "an entry must be an object";
1423
1230
  const entry = e;
1424
- const extra = Object.keys(entry).filter((k) => !LEASE_ENTRY_KEYS.includes(k));
1425
- if (extra.length > 0) fail(`an entry carries an undeclared field (${extra.join(", ")})`);
1426
- const { holder, generation, until } = entry;
1427
- if (typeof holder !== "string" || !HOLDER.test(holder)) {
1428
- fail("a holder must be 16 lowercase hex digits");
1429
- }
1430
- if (seen.has(holder)) fail("holders must be unique");
1431
- seen.add(holder);
1432
- for (const [name, n] of [
1433
- ["generation", generation],
1434
- ["until", until]
1435
- ]) {
1436
- if (!Number.isSafeInteger(n) || n < 0) {
1437
- fail(`${name} must be a non-negative safe integer (got ${String(n)})`);
1438
- }
1231
+ const extra = Object.keys(entry).filter((k) => !ENTRY_KEYS.includes(k));
1232
+ if (extra.length > 0) {
1233
+ return `an entry carries an undeclared field (${show(extra.join(", "))})`;
1439
1234
  }
1440
- out.push(
1441
- origin === "stored" ? entry : Object.freeze({
1442
- holder,
1443
- generation,
1444
- until
1445
- })
1446
- );
1447
- }
1448
- if (origin === "stored") return out;
1449
- return out.length === 0 ? void 0 : Object.freeze(out);
1450
- }
1451
- function validateLeasesPointer(leases, currentGen) {
1452
- if (leases !== void 0 && leases.length > 0 && currentGen === null) {
1453
- throw new ValidationError("leases: a row with no pointer has no generation to lease");
1454
- }
1455
- }
1456
- function validateKnownGensFit(knownGens, currentGen, summary) {
1457
- if (knownGens === void 0) return;
1458
- if (currentGen === null) {
1459
- throw new ValidationError("knownGens: a row with no pointer has no generation to know");
1460
- }
1461
- const last = knownGens[knownGens.length - 1];
1462
- if (last === void 0 || last.generation !== currentGen) {
1463
- throw new ValidationError(
1464
- `knownGens must end with the row's pointer ${currentGen}, in the order the generations were made current (its last entry is ${last === void 0 ? "missing" : last.generation})`
1465
- );
1466
- }
1467
- if (summary !== void 0 && summary.generation === currentGen && "fingerprint" in summary && fingerprintParts(summary.fingerprint)?.footerCrc !== last.footerCrc) {
1468
- throw new ValidationError(
1469
- `knownGens records footerCrc ${last.footerCrc} for generation ${currentGen}, but the summary's fingerprint ${summary.fingerprint} carries another: both describe the one current object`
1470
- );
1471
- }
1472
- }
1473
- function refuseDriverManaged(write, what) {
1474
- const named = DRIVER_MANAGED.filter((field) => field in write);
1475
- if (named.length > 0) {
1476
- throw new ValidationError(
1477
- `${what} names ${named.join(" and ")}, which the registry sets on every write and a caller never does`
1478
- );
1479
- }
1480
- }
1481
- function validateKeyId(keyId) {
1482
- if (keyId !== void 0 && (typeof keyId !== "string" || keyId.length === 0)) {
1483
- throw new ValidationError("keyId must be a non-empty string, or absent");
1484
- }
1485
- }
1486
- function validateNewRegistryRecord(rec) {
1487
- refuseDriverManaged(rec, "a new record");
1488
- validateKeyId(rec.keyId);
1489
- validateGeneration(rec.currentGen);
1490
- if (rec.status !== void 0) validateStatus(rec.status);
1491
- validateWrappedDeks(rec.wrappedDeks, "caller");
1492
- validateGovernance(rec.retention, "retention");
1493
- validateGovernance(rec.residency, "residency");
1494
- const knownGens = validateKnownGens(rec.knownGens, "caller");
1495
- const withKnown = knownGens === void 0 ? rec : { ...rec, knownGens };
1496
- const summary = "summary" in rec ? validateSummary(rec.summary, "caller") : void 0;
1497
- validateKnownGensFit(knownGens, rec.currentGen, summary);
1498
- if (!("summary" in rec)) return withKnown;
1499
- validateSummaryNames(summary, rec.currentGen);
1500
- validateSummaryKeys(summary, rec.wrappedDeks);
1501
- return { ...withKnown, summary };
1502
- }
1503
- function validateRegistryPatch(patch) {
1504
- refuseDriverManaged(patch, "a patch");
1505
- if ("keyId" in patch) validateKeyId(patch.keyId);
1506
- validatePatchGeneration(patch);
1507
- if (patch.status !== void 0) validateStatus(patch.status);
1508
- if ("wrappedDeks" in patch) validateWrappedDeks(patch.wrappedDeks, "caller");
1509
- if ("retention" in patch) validateGovernance(patch.retention, "retention");
1510
- if ("residency" in patch) validateGovernance(patch.residency, "residency");
1511
- const withKnown = patch.knownGens === void 0 ? patch : { ...patch, knownGens: validateKnownGens(patch.knownGens, "caller") };
1512
- const withLeases = "leases" in withKnown ? { ...withKnown, leases: validateLeases(withKnown.leases, "caller") } : withKnown;
1513
- if (!("summary" in patch)) return withLeases;
1514
- return { ...withLeases, summary: validateSummary(patch.summary, "caller") };
1515
- }
1516
- var REGISTRY_SCHEMA_VERSION = 5;
1517
- function assertRegistrySchemaVersion(raw, ctx) {
1518
- if (raw === void 0) {
1519
- throw new IntegrityError(`registry row has no schemaVersion: ${ctx}`);
1520
- }
1521
- if (typeof raw !== "number" || !Number.isInteger(raw) || raw < 1) {
1522
- throw new IntegrityError(`registry row has a malformed schemaVersion (${shown3(raw)}): ${ctx}`);
1523
- }
1524
- if (raw < REGISTRY_SCHEMA_VERSION) {
1525
- throw new UnsupportedError(
1526
- `registry row schemaVersion ${raw} is one this build does not read: it reads schema ${REGISTRY_SCHEMA_VERSION} only. Export the segments with the release that wrote the row and load them with this one into a new prefix (steps: https://github.com/cloudbitmaps/cloudbitmaps/blob/main/CHANGELOG.md, the Breaking entry on registry schema 5): ${ctx}`
1527
- );
1528
- }
1529
- if (raw > REGISTRY_SCHEMA_VERSION) {
1530
- throw new UnsupportedError(
1531
- `registry row schemaVersion ${raw} is newer than this build reads (v${REGISTRY_SCHEMA_VERSION}): ${ctx}`
1532
- );
1533
- }
1534
- return raw;
1535
- }
1536
- function serializeRegistryEnvelope(env) {
1537
- return JSON.stringify({
1538
- schemaVersion: REGISTRY_SCHEMA_VERSION,
1539
- deleted: env.deleted,
1540
- record: env.record
1541
- });
1542
- }
1543
- var INCARNATION_BYTES = 16;
1544
- var WRITE_BYTES = 8;
1545
- function tokenParts(token, ctx, what = "token") {
1546
- const match = typeof token === "string" ? INCARNATION_TOKEN.exec(token) : null;
1547
- if (match === null) {
1548
- throw new IntegrityError(
1549
- `registry row ${what} is not one a shipped registry writes (${shown3(JSON.stringify(token))}): ${ctx}`
1550
- );
1551
- }
1552
- const counter = Number(match[2]);
1553
- if (!Number.isSafeInteger(counter)) {
1554
- throw new IntegrityError(`registry row ${what}'s counter is out of safe-integer range: ${ctx}`);
1555
- }
1556
- return { incarnation: match[1], counter };
1557
- }
1558
- function checkPointerId(pointerId, token, ctx) {
1559
- const parts = tokenParts(pointerId, ctx, "pointerId");
1560
- if (parts.incarnation !== token.incarnation) {
1561
- throw new IntegrityError(
1562
- `registry row pointerId names another incarnation than its token: ${ctx}`
1563
- );
1564
- }
1565
- if (parts.counter > token.counter) {
1566
- throw new IntegrityError(`registry row pointerId is a write after its token: ${ctx}`);
1567
- }
1568
- }
1569
- function drawHex(entropy, bytes) {
1570
- const drawn = entropy(bytes);
1571
- if (!(drawn instanceof Uint8Array) || drawn.length !== bytes) {
1572
- throw new ValidationError(
1573
- `the registry's entropy source must return ${bytes} bytes as a Uint8Array`
1574
- );
1575
- }
1576
- let hex = "";
1577
- for (const b of drawn) hex += b.toString(16).padStart(2, "0");
1578
- return hex;
1579
- }
1580
- function drawIncarnation(entropy) {
1581
- return drawHex(entropy, INCARNATION_BYTES);
1582
- }
1583
- function drawWrite(entropy) {
1584
- return drawHex(entropy, WRITE_BYTES);
1585
- }
1586
- function nextRegistryToken(record, entropy) {
1587
- const { incarnation, counter } = tokenParts(record.token, record.segment);
1588
- return `${incarnation}.${counter + 1}.${drawWrite(entropy)}`;
1589
- }
1590
- function newIncarnationToken(entropy, tombstone) {
1591
- const counter = tombstone === void 0 ? 0 : tokenParts(tombstone.token, tombstone.segment).counter + 1;
1592
- return `${drawIncarnation(entropy)}.${counter}.${drawWrite(entropy)}`;
1593
- }
1594
- function shown3(v) {
1595
- const text = String(v);
1596
- return text.length > 64 ? `${text.slice(0, 64)}\u2026` : text;
1597
- }
1598
- function parseRegistryEnvelope(text, ctx, expected) {
1599
- let parsed;
1600
- try {
1601
- parsed = JSON.parse(text);
1602
- } catch {
1603
- throw new IntegrityError(`registry row is not valid JSON: ${ctx}`);
1604
- }
1605
- if (parsed === null || typeof parsed !== "object") {
1606
- throw new IntegrityError(`registry row has a malformed envelope: ${ctx}`);
1607
- }
1608
- const env = parsed;
1609
- assertRegistrySchemaVersion(env.schemaVersion, ctx);
1610
- const extra = Object.keys(env).filter((k) => !ENVELOPE_FIELDS.includes(k));
1611
- if (extra.length > 0) {
1612
- throw new IntegrityError(
1613
- `registry row has fields its envelope does not declare (${shown3(extra.join(", "))}): ${ctx}`
1614
- );
1615
- }
1616
- if (typeof env.deleted !== "boolean" || env.record === null || typeof env.record !== "object") {
1617
- throw new IntegrityError(`registry row has a malformed envelope: ${ctx}`);
1618
- }
1619
- const r = env.record;
1620
- assertStoredRecordShape(r, ctx);
1621
- if (typeof r.token !== "string") {
1622
- throw new IntegrityError(`registry row is missing its token: ${ctx}`);
1623
- }
1624
- checkPointerId(r.pointerId, tokenParts(r.token, ctx), ctx);
1625
- if (expected !== void 0 && (r.segment !== expected.segment || (r.namespace ?? void 0) !== (expected.namespace ?? void 0))) {
1626
- throw new IntegrityError(
1627
- `registry row names another segment than the one it is stored for: ${ctx}`
1628
- );
1629
- }
1630
- return { deleted: env.deleted, record: env.record };
1631
- }
1632
- function recordFromNew(ref, rec, now, token) {
1633
- return {
1634
- namespace: ref.namespace,
1635
- segment: ref.segment,
1636
- currentGen: rec.currentGen,
1637
- wrappedDeks: rec.wrappedDeks,
1638
- keyId: rec.keyId,
1639
- status: rec.status ?? "active",
1640
- retention: rec.retention,
1641
- residency: rec.residency,
1642
- summary: rec.summary,
1643
- knownGens: rec.knownGens,
1644
- // A new row has no holder: a lease is written only against a row that already exists.
1645
- leases: void 0,
1646
- createdAt: now,
1647
- updatedAt: now,
1648
- token,
1649
- pointerId: token
1650
- };
1651
- }
1652
- function assertStoredRecordShape(r, ctx) {
1653
- if (typeof r.segment !== "string" || typeof r.pointerId !== "string" || r.namespace !== void 0 && typeof r.namespace !== "string" || r.currentGen !== null && typeof r.currentGen !== "number" || typeof r.status !== "string" || typeof r.createdAt !== "number" || typeof r.updatedAt !== "number") {
1654
- throw new IntegrityError(`registry record is missing required fields: ${ctx}`);
1655
- }
1656
- if (r.currentGen !== null && (!Number.isSafeInteger(r.currentGen) || r.currentGen < 0)) {
1657
- throw new IntegrityError(
1658
- `registry record has an invalid currentGen (${shown3(r.currentGen)}): ${ctx}`
1659
- );
1660
- }
1661
- if (!STATUSES.includes(r.status)) {
1662
- throw new IntegrityError(`registry record has an unknown status (${shown3(r.status)}): ${ctx}`);
1663
- }
1664
- const declared = RECORD_FIELDS;
1665
- const extra = Object.keys(r).filter((k) => !declared.includes(k));
1666
- if (extra.length > 0) {
1667
- throw new IntegrityError(
1668
- `registry record has fields a schema-${REGISTRY_SCHEMA_VERSION} row does not declare (${shown3(extra.join(", "))}): ${ctx}`
1669
- );
1670
- }
1671
- if (r.keyId !== void 0 && typeof r.keyId !== "string") {
1672
- throw new IntegrityError(`registry record has an invalid keyId: ${ctx}`);
1673
- }
1674
- validateWrappedDeks(r.wrappedDeks, "stored");
1675
- for (const field of ["retention", "residency"]) {
1676
- const meta = r[field];
1677
- if (meta !== void 0 && (meta === null || typeof meta !== "object" || Array.isArray(meta))) {
1678
- throw new IntegrityError(`registry record has a non-object ${field}: ${ctx}`);
1235
+ const { generation, footerCrc } = entry;
1236
+ if (!Number.isSafeInteger(generation) || generation < 0) {
1237
+ return `a generation must be a non-negative safe integer (got ${show(generation)})`;
1679
1238
  }
1680
- }
1681
- validateSummary(r.summary, "stored", ctx);
1682
- validateKnownGens(r.knownGens, "stored", ctx);
1683
- validateLeases(r.leases, "stored", ctx);
1684
- }
1685
- function applyRegistryPatch(prev, patch, now, token) {
1686
- const currentGen = "currentGen" in patch ? patch.currentGen ?? null : prev.currentGen;
1687
- const wrappedDeks = "wrappedDeks" in patch ? patch.wrappedDeks : prev.wrappedDeks;
1688
- let summary;
1689
- if ("summary" in patch) {
1690
- validateSummaryNames(patch.summary, currentGen);
1691
- validateSummaryKeys(patch.summary, wrappedDeks);
1692
- summary = patch.summary;
1693
- } else {
1694
- const kept = currentGen === prev.currentGen ? prev.summary : void 0;
1695
- summary = kept !== void 0 && summaryAgreesWithKeys(kept, wrappedDeks) ? kept : void 0;
1696
- }
1697
- let knownGens;
1698
- if ("knownGens" in patch) {
1699
- validateKnownGensFit(patch.knownGens, currentGen, summary);
1700
- knownGens = patch.knownGens;
1701
- } else {
1702
- knownGens = currentGen === prev.currentGen ? prev.knownGens : void 0;
1703
- }
1704
- let leases;
1705
- if ("leases" in patch) {
1706
- leases = patch.leases;
1707
- validateLeasesPointer(leases, currentGen);
1708
- const most = Math.max(MAX_LEASES_PER_SEGMENT, prev.leases?.length ?? 0);
1709
- if (leases !== void 0 && leases.length > most) {
1710
- throw new ValidationError(`leases: has ${leases.length} entries, cap ${most}`);
1239
+ if (!Number.isInteger(footerCrc) || footerCrc < 0 || footerCrc > 4294967295) {
1240
+ return `a footerCrc must be an integer from 0 to 2^32 - 1 (got ${show(footerCrc)})`;
1711
1241
  }
1712
- } else {
1713
- leases = prev.leases;
1714
- }
1715
- return {
1716
- namespace: prev.namespace,
1717
- segment: prev.segment,
1718
- currentGen,
1719
- wrappedDeks,
1720
- keyId: "keyId" in patch ? patch.keyId : prev.keyId,
1721
- status: patch.status ?? prev.status,
1722
- retention: "retention" in patch ? patch.retention : prev.retention,
1723
- residency: "residency" in patch ? patch.residency : prev.residency,
1724
- summary,
1725
- knownGens,
1726
- leases,
1727
- createdAt: prev.createdAt,
1728
- updatedAt: now,
1729
- token,
1730
- pointerId: renewsPointer(patch) ? token : prev.pointerId
1731
- };
1732
- }
1733
-
1734
- // src/drivers/_shared/entropy.ts
1735
- function webCrypto() {
1736
- const c = globalThis.crypto;
1737
- return c !== void 0 && typeof c.getRandomValues === "function" ? c : void 0;
1738
- }
1739
- var webCryptoEntropy = (length) => {
1740
- const c = webCrypto();
1741
- if (c === void 0) {
1742
- throw new UnsupportedError(
1743
- "this runtime has no Web Crypto (crypto.getRandomValues), which a registry needs for the token of every row it writes: run on a runtime that has it"
1744
- );
1242
+ if (seen.has(generation)) return `generation ${String(generation)} is repeated`;
1243
+ seen.add(generation);
1745
1244
  }
1746
- return c.getRandomValues(new Uint8Array(length));
1747
- };
1748
- function entropyIsAvailable(entropy) {
1749
- return entropy !== webCryptoEntropy || webCrypto() !== void 0;
1245
+ return void 0;
1750
1246
  }
1751
1247
 
1752
1248
  export {
1753
1249
  CloudRoaringError,
1754
1250
  ValidationError,
1251
+ CONTENTION_NEXT_STEP,
1755
1252
  WriteConflictError,
1756
1253
  IntegrityError,
1757
1254
  NotFoundError,
@@ -1763,29 +1260,30 @@ export {
1763
1260
  BudgetExceededError,
1764
1261
  KeyUnavailableError,
1765
1262
  TransientError,
1766
- isCloudRoaringError,
1767
1263
  isWriteConflictError,
1264
+ unsettledConflict,
1265
+ isUnsettledConflict,
1768
1266
  isTransientError,
1769
- isLeaseExpiredError,
1770
- isStaleOperandError,
1771
- isLeaseLimitError,
1772
1267
  isNotFoundError,
1773
1268
  isIntegrityError,
1774
1269
  isValidationError,
1775
- isUnsupportedError,
1776
- isCapabilityError,
1777
- isBudgetExceededError,
1778
1270
  isKeyUnavailableError,
1271
+ errorKind,
1272
+ shown,
1779
1273
  requireInteger,
1274
+ checkRequireEncryption,
1780
1275
  requirePositiveInteger,
1781
1276
  requireFiniteNumber,
1277
+ refuseUnknownOptions,
1278
+ requireBool,
1279
+ requireNonEmptyString,
1782
1280
  U32_MAX,
1783
1281
  CHUNK_COUNT,
1282
+ isBadIdError,
1283
+ refusalOfId,
1784
1284
  splitId,
1785
1285
  MAX_REMAINDER,
1786
1286
  joinId,
1787
- mapWithConcurrency,
1788
- boundedAll,
1789
1287
  PREAMBLE_BYTES,
1790
1288
  FOOTER_BYTES,
1791
1289
  encodeNameForKey,
@@ -1798,6 +1296,8 @@ export {
1798
1296
  RESERVED_NAMESPACE_PREFIX,
1799
1297
  isReservedNamespace,
1800
1298
  validateUserNamespace,
1299
+ PORT_KEYS,
1300
+ refuseUnknownRefKeys,
1801
1301
  validateUserRef,
1802
1302
  validateChunkKeyOrder,
1803
1303
  validateChunkRef,
@@ -1806,15 +1306,7 @@ export {
1806
1306
  STORAGE_BACKEND,
1807
1307
  brandAsBackend,
1808
1308
  isStorageBackend,
1809
- MAX_METADATA_BYTES,
1810
- metadataBytes,
1811
- copiedMetadata,
1812
- metadataFromBytes,
1813
- sizePart,
1814
- fingerprintFor,
1815
- fingerprintParts,
1816
- footerCrcOf,
1817
- compactFingerprint,
1309
+ INCARNATION_TOKEN,
1818
1310
  incarnationOf,
1819
1311
  incarnationField,
1820
1312
  sameIncarnation,
@@ -1822,14 +1314,28 @@ export {
1822
1314
  renewsPointer,
1823
1315
  renewPointer,
1824
1316
  pointerIdOf,
1317
+ pointerSetBy,
1825
1318
  aadFor,
1826
1319
  sameKeys,
1320
+ MAX_METADATA_BYTES,
1321
+ canonicalMetadataJson,
1322
+ metadataBytes,
1323
+ copiedMetadata,
1324
+ metadataFromBytes,
1325
+ sizePart,
1326
+ fingerprintFor,
1327
+ fingerprintParts,
1328
+ footerCrcOf,
1329
+ compactFingerprint,
1330
+ MAX_SUMMARY_CARDINALITY,
1331
+ tailBytesFit,
1827
1332
  sizeOfFingerprint,
1828
1333
  usableSummary,
1829
1334
  summaryAgrees,
1830
1335
  metadataToCarry,
1831
1336
  summaryOf,
1832
1337
  ownSummary,
1338
+ ascending,
1833
1339
  DEFAULT_RETRY_POLICY,
1834
1340
  jitteredWait,
1835
1341
  retryRead,
@@ -1838,6 +1344,7 @@ export {
1838
1344
  MAX_LEASE_MS,
1839
1345
  LEASE_SKEW_MS,
1840
1346
  MAX_LEASES_PER_SEGMENT,
1347
+ MAX_STORED_LEASES,
1841
1348
  UNANSWERED_RESENDS,
1842
1349
  UNANSWERED_BASE_MS,
1843
1350
  isLive,
@@ -1846,8 +1353,7 @@ export {
1846
1353
  leaseChurn,
1847
1354
  takeLease,
1848
1355
  releaseLease,
1849
- knownGensShapeFault,
1850
- ascending,
1356
+ MAX_KNOWN_GENERATIONS,
1851
1357
  MAX_STORED_KNOWN_GENERATIONS,
1852
1358
  usableKnownGens,
1853
1359
  windowOf,
@@ -1859,24 +1365,6 @@ export {
1859
1365
  describingKnown,
1860
1366
  isFullLedger,
1861
1367
  fallbackBound,
1862
- validateNewRegistryRecord,
1863
- validateRegistryPatch,
1864
- serializeRegistryEnvelope,
1865
- drawIncarnation,
1866
- drawWrite,
1867
- nextRegistryToken,
1868
- newIncarnationToken,
1869
- parseRegistryEnvelope,
1870
- recordFromNew,
1871
- applyRegistryPatch,
1872
- webCryptoEntropy,
1873
- entropyIsAvailable,
1874
- DEFAULT_NAMESPACE,
1875
- namespaceKeyPart,
1876
- namespacePathPart,
1877
- refuseUnknownOptions,
1878
- checkNow,
1879
- requirePositiveInt,
1880
- requireBool
1368
+ knownGensShapeFault
1881
1369
  };
1882
- //# sourceMappingURL=chunk-IGLIZBR7.js.map
1370
+ //# sourceMappingURL=chunk-MVUD4R72.js.map