sliftutils 1.7.32 → 1.7.34

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 (116) hide show
  1. package/builders/extensionBuild.ts +1 -1
  2. package/bundler/bundleEntry.ts +1 -3
  3. package/bundler/bundler.ts +2 -4
  4. package/bundler/sourceMaps.ts +1 -2
  5. package/examplestorage/browser.tsx +1 -2
  6. package/examplestorage/exampleserver.ts +1 -2
  7. package/index.d.ts +134 -102
  8. package/misc/dist/environment.ts.cache +2 -2
  9. package/misc/dist/getSecret.ts.cache +2 -2
  10. package/misc/dist/strings.ts.cache +2 -2
  11. package/misc/dist/zip.ts.cache +2 -2
  12. package/misc/https/dist/certs.ts.cache +2 -2
  13. package/misc/https/dist/dns.ts.cache +7 -16
  14. package/misc/https/dist/persistentLocalStorage.ts.cache +2 -2
  15. package/misc/https/dns.d.ts +1 -4
  16. package/misc/https/dns.ts +5 -14
  17. package/misc/https/hostServer.d.ts +2 -0
  18. package/misc/https/hostServer.ts +6 -7
  19. package/misc/https/httpsCerts.ts +4 -10
  20. package/misc/openrouter.ts +1 -2
  21. package/package.json +1 -1
  22. package/render-utils/FullscreenModal.tsx +1 -4
  23. package/render-utils/GenericFormat.tsx +2 -8
  24. package/render-utils/Input.tsx +5 -13
  25. package/render-utils/InputLabel.tsx +4 -6
  26. package/render-utils/InputPicker.tsx +1 -2
  27. package/render-utils/URLParam.ts +2 -3
  28. package/render-utils/dist/observer.tsx.cache +2 -2
  29. package/render-utils/mobxTyped.ts +2 -3
  30. package/render-utils/niceStringify.ts +2 -4
  31. package/storage/ArchivesDisk.ts +4 -14
  32. package/storage/BulkDatabase2/BulkDatabase2.d.ts +1 -2
  33. package/storage/BulkDatabase2/BulkDatabase2.ts +4 -11
  34. package/storage/BulkDatabase2/BulkDatabaseBase.ts +42 -114
  35. package/storage/BulkDatabase2/BulkDatabaseFormat.ts +31 -91
  36. package/storage/BulkDatabase2/BulkDatabaseMerge.ts +19 -57
  37. package/storage/BulkDatabase2/BulkDatabaseReader.ts +4 -12
  38. package/storage/BulkDatabase2/LoadedIndex.ts +6 -18
  39. package/storage/BulkDatabase2/WriteOverlay.ts +3 -8
  40. package/storage/BulkDatabase2/blockCache.ts +12 -28
  41. package/storage/BulkDatabase2/dist/BulkDatabaseBase.ts.cache +40 -103
  42. package/storage/BulkDatabase2/dist/BulkDatabaseFormat.ts.cache +19 -50
  43. package/storage/BulkDatabase2/dist/BulkDatabaseMerge.ts.cache +18 -50
  44. package/storage/BulkDatabase2/dist/BulkDatabaseReader.ts.cache +7 -15
  45. package/storage/BulkDatabase2/dist/LoadedIndex.ts.cache +9 -21
  46. package/storage/BulkDatabase2/dist/WriteOverlay.ts.cache +6 -11
  47. package/storage/BulkDatabase2/dist/blockCache.ts.cache +15 -31
  48. package/storage/BulkDatabase2/dist/mergeLock.ts.cache +10 -29
  49. package/storage/BulkDatabase2/dist/mergeMarkers.ts.cache +10 -18
  50. package/storage/BulkDatabase2/dist/streamLog.ts.cache +17 -43
  51. package/storage/BulkDatabase2/dist/syncClient.ts.cache +11 -27
  52. package/storage/BulkDatabase2/mergeLock.ts +7 -26
  53. package/storage/BulkDatabase2/mergeMarkers.ts +7 -15
  54. package/storage/BulkDatabase2/streamLog.ts +14 -40
  55. package/storage/BulkDatabase2/syncClient.ts +8 -24
  56. package/storage/FileFolderAPI.tsx +28 -75
  57. package/storage/IArchives.d.ts +3 -3
  58. package/storage/IArchives.ts +21 -50
  59. package/storage/IStorage.ts +2 -4
  60. package/storage/PendingManager.tsx +2 -3
  61. package/storage/TransactionStorage.ts +11 -24
  62. package/storage/backblaze.ts +12 -43
  63. package/storage/dist/ArchivesDisk.ts.cache +7 -17
  64. package/storage/dist/FileFolderAPI.tsx.cache +25 -63
  65. package/storage/dist/IArchives.ts.cache +10 -20
  66. package/storage/dist/IndexedDBFileFolderAPI.ts.cache +2 -2
  67. package/storage/dist/JSONStorage.ts.cache +2 -2
  68. package/storage/dist/PendingManager.tsx.cache +5 -6
  69. package/storage/dist/TransactionStorage.ts.cache +14 -27
  70. package/storage/dist/backblaze.ts.cache +15 -46
  71. package/storage/dist/embeddingFormats.ts.cache +11 -24
  72. package/storage/dist/fileSystemPointer.ts.cache +4 -7
  73. package/storage/dist/remoteFileStorage.ts.cache +17 -36
  74. package/storage/embeddingFormats.ts +11 -27
  75. package/storage/fileSystemPointer.ts +2 -7
  76. package/storage/proxydatabase/dist/ivfEmbeddingDatabase.ts.cache +8 -18
  77. package/storage/proxydatabase/dist/transactionSet.ts.cache +4 -7
  78. package/storage/proxydatabase/inMemoryDatabase.ts +1 -3
  79. package/storage/proxydatabase/ivfEmbeddingDatabase.ts +8 -23
  80. package/storage/proxydatabase/transactionSet.ts +2 -5
  81. package/storage/remoteFileServer.ts +20 -48
  82. package/storage/remoteFileStorage.ts +14 -33
  83. package/storage/remoteStorage/ArchivesRemote.d.ts +1 -4
  84. package/storage/remoteStorage/ArchivesRemote.ts +11 -26
  85. package/storage/remoteStorage/ArchivesUrl.ts +2 -4
  86. package/storage/remoteStorage/accessPage.tsx +1 -5
  87. package/storage/remoteStorage/blobStore.d.ts +5 -13
  88. package/storage/remoteStorage/blobStore.ts +52 -130
  89. package/storage/remoteStorage/createArchives.d.ts +22 -14
  90. package/storage/remoteStorage/createArchives.ts +54 -188
  91. package/storage/remoteStorage/deployTakeover.d.ts +18 -32
  92. package/storage/remoteStorage/deployTakeover.ts +70 -384
  93. package/storage/remoteStorage/dist/ArchivesRemote.ts.cache +12 -24
  94. package/storage/remoteStorage/dist/ArchivesUrl.ts.cache +5 -7
  95. package/storage/remoteStorage/dist/blobStore.ts.cache +47 -117
  96. package/storage/remoteStorage/dist/cliArgs.ts.cache +2 -2
  97. package/storage/remoteStorage/dist/createArchives.ts.cache +58 -189
  98. package/storage/remoteStorage/dist/deployTakeover.ts.cache +81 -387
  99. package/storage/remoteStorage/dist/intermediateSources.ts.cache +119 -0
  100. package/storage/remoteStorage/dist/remoteConfig.ts.cache +36 -24
  101. package/storage/remoteStorage/dist/sourceWrapper.ts.cache +20 -40
  102. package/storage/remoteStorage/dist/storageClientController.ts.cache +4 -6
  103. package/storage/remoteStorage/dist/storageController.ts.cache +37 -74
  104. package/storage/remoteStorage/dist/storageServerState.ts.cache +345 -188
  105. package/storage/remoteStorage/grantAccessCli.ts +1 -3
  106. package/storage/remoteStorage/intermediateSources.d.ts +21 -0
  107. package/storage/remoteStorage/intermediateSources.ts +117 -0
  108. package/storage/remoteStorage/remoteConfig.ts +33 -20
  109. package/storage/remoteStorage/sourceWrapper.d.ts +8 -19
  110. package/storage/remoteStorage/sourceWrapper.ts +19 -41
  111. package/storage/remoteStorage/storageClientController.ts +1 -3
  112. package/storage/remoteStorage/storageController.d.ts +7 -6
  113. package/storage/remoteStorage/storageController.ts +36 -82
  114. package/storage/remoteStorage/storageServer.ts +20 -34
  115. package/storage/remoteStorage/storageServerState.d.ts +41 -5
  116. package/storage/remoteStorage/storageServerState.ts +355 -216
@@ -39,10 +39,7 @@ const USE_INDEXED_DB = false;
39
39
  // How often a worker rechecks the pointer IndexedDB for a granted handle when none is available yet.
40
40
  const WORKER_POLL_INTERVAL_MS = 60 * 1000;
41
41
 
42
- // These mirror the subset of the native FileSystemFileHandle / FileSystemDirectoryHandle API we use, so
43
- // the native browser handles, the Node handles, and the remote handles are all interchangeable — and
44
- // code written against the native handle (e.g. a recursive walk over `handle.entries()`) works on any of
45
- // them. kind/name and entries() are part of that contract.
42
+ // These mirror the subset of the native FileSystemFileHandle / FileSystemDirectoryHandle API we use, so the native browser handles, the Node handles, and the remote handles are all interchangeable — and code written against the native handle (e.g. a recursive walk over `handle.entries()`) works on any of them. kind/name and entries() are part of that contract.
46
43
  export type FileWrapper = {
47
44
  readonly kind: "file";
48
45
  readonly name: string;
@@ -59,19 +56,15 @@ export type FileWrapper = {
59
56
  write(value: Buffer): Promise<void>;
60
57
  close(): Promise<void>;
61
58
  }>;
62
- // Returns a URL for the file's bytes, usable in <video>/<img>/fetch. Optional — the native browser
63
- // FileSystemFileHandle has no such method, so prefer the getFileURL() helper, which falls back to a
64
- // blob: URL via createObjectURL for native handles. Always release it with disposeFileURL when done.
59
+ // Returns a URL for the file's bytes, usable in <video>/<img>/fetch. Optional — the native browser FileSystemFileHandle has no such method, so prefer the getFileURL() helper, which falls back to a blob: URL via createObjectURL for native handles. Always release it with disposeFileURL when done.
65
60
  getURL?(): Promise<string>;
66
61
  };
67
62
  export type DirectoryWrapper = {
68
63
  readonly kind: "directory";
69
64
  readonly name: string;
70
- // Full path from the storage root, for diagnostics/logging (the native handle doesn't expose paths,
71
- // so it's optional). e.g. "bulkDatabases2/myCollection".
65
+ // Full path from the storage root, for diagnostics/logging (the native handle doesn't expose paths, so it's optional). e.g. "bulkDatabases2/myCollection".
72
66
  readonly fullPath?: string;
73
- // True when this storage is served over the network (a remote server) rather than a local disk, so
74
- // callers can adapt for the higher latency/cost (local/native handles leave it undefined = false).
67
+ // True when this storage is served over the network (a remote server) rather than a local disk, so callers can adapt for the higher latency/cost (local/native handles leave it undefined = false).
75
68
  readonly isRemote?: boolean;
76
69
  removeEntry(key: string, options?: { recursive?: boolean }): Promise<void>;
77
70
  getFileHandle(key: string, options?: { create?: boolean }): Promise<FileWrapper>;
@@ -107,8 +100,7 @@ export function setStorageRootOverride(handle: FileSystemDirectoryHandle | undef
107
100
  }
108
101
 
109
102
  // ---- remote (server) storage config ----
110
- // Instead of a local folder, the user can point at a remoteFileServer.js instance (URL + password).
111
- // When configured, getFileStorageNested2 serves everything from that server. Persisted in localStorage.
103
+ // Instead of a local folder, the user can point at a remoteFileServer.js instance (URL + password). When configured, getFileStorageNested2 serves everything from that server. Persisted in localStorage.
112
104
  type RemoteConfig = { url: string; password: string };
113
105
  function remoteConfigKey() { return getFileAPIKey() + ":remote"; }
114
106
  function loadRemoteConfig(): RemoteConfig | undefined {
@@ -122,8 +114,7 @@ function loadRemoteConfig(): RemoteConfig | undefined {
122
114
  }
123
115
  function saveRemoteConfig(c: RemoteConfig) { localStorage.setItem(remoteConfigKey(), JSON.stringify(c)); }
124
116
 
125
- // The server is always HTTPS on the filehoster's default port, so the user only needs to type the host
126
- // (e.g. "65.109.93.113"). We strip any scheme/path they include and default the port if omitted.
117
+ // The server is always HTTPS on the filehoster's default port, so the user only needs to type the host (e.g. "65.109.93.113"). We strip any scheme/path they include and default the port if omitted.
127
118
  const DEFAULT_REMOTE_PORT = 8787; // matches remoteFileServer.js's default
128
119
  function normalizeServerUrl(raw: string): string {
129
120
  let s = raw.trim().replace(/^\w+:\/\//, "").replace(/\/.*$/, "");
@@ -160,11 +151,7 @@ class DirectoryPrompter extends preact.Component {
160
151
  }
161
152
  }
162
153
 
163
- // "Connect to a server" option for the directory prompt: collapses to a button, expands to address +
164
- // password fields. On connect it ACTUALLY connects (testRemoteConnection); only on success does it
165
- // persist the config and call onConnected, so the caller (getDirectoryHandle) resolves with a working
166
- // remote handle. Failures are shown to the user (and logged, without the password), never swallowed.
167
- // `initial` pre-fills + expands the form (used to retry a remembered server that stopped working).
154
+ // "Connect to a server" option for the directory prompt: collapses to a button, expands to address + password fields. On connect it ACTUALLY connects (testRemoteConnection); only on success does it persist the config and call onConnected, so the caller (getDirectoryHandle) resolves with a working remote handle. Failures are shown to the user (and logged, without the password), never swallowed. `initial` pre-fills + expands the form (used to retry a remembered server that stopped working).
168
155
  @observer
169
156
  class ServerConnectForm extends preact.Component<{ onConnected: (config: RemoteConfig) => void; initial?: RemoteConfig }> {
170
157
  private obs = observable({
@@ -193,8 +180,7 @@ class ServerConnectForm extends preact.Component<{ onConnected: (config: RemoteC
193
180
  s.error = "The server rejected that password.";
194
181
  console.error("Remote connect: unauthorized for", url);
195
182
  } else {
196
- // Got nothing back — usually the self-signed certificate isn't trusted yet, but show the
197
- // actual error too so a wrong address / down server / CORS issue is diagnosable.
183
+ // Got nothing back — usually the self-signed certificate isn't trusted yet, but show the actual error too so a wrong address / down server / CORS issue is diagnosable.
198
184
  s.needsCert = true;
199
185
  s.error = "Couldn't reach the server: " + result.error;
200
186
  console.error("Remote connect: unreachable", url, "-", result.error);
@@ -343,9 +329,7 @@ export class NodeJSDirectoryHandleWrapper implements DirectoryWrapper {
343
329
  // Create the file
344
330
  await fs.promises.writeFile(filePath, Buffer.alloc(0));
345
331
  } else if (!exists) {
346
- // Tag as ENOENT so readWithRetry treats it as a genuinely-missing file (return undefined now)
347
- // rather than a transient read failure to retry 6× with backoff — missing files are normal
348
- // (a concurrent merge deletes a file mid-read), and retrying them is catastrophically slow.
332
+ // Tag as ENOENT so readWithRetry treats it as a genuinely-missing file (return undefined now) rather than a transient read failure to retry 6× with backoff — missing files are normal (a concurrent merge deletes a file mid-read), and retrying them is catastrophically slow.
349
333
  const err = new Error(`File not found: ${filePath}`) as Error & { code?: string };
350
334
  err.code = "ENOENT";
351
335
  throw err;
@@ -387,9 +371,7 @@ export class NodeJSDirectoryHandleWrapper implements DirectoryWrapper {
387
371
  }
388
372
 
389
373
 
390
- // When set, getDirectoryHandle skips every prompt + remote check and serves from the browser's Origin
391
- // Private File System (OPFS). One named subfolder is "current" per fileAPIKey; pickPrivateFolder /
392
- // resetStorageLocation switch which one. Set this in app startup BEFORE the first getDirectoryHandle.
374
+ // When set, getDirectoryHandle skips every prompt + remote check and serves from the browser's Origin Private File System (OPFS). One named subfolder is "current" per fileAPIKey; pickPrivateFolder / resetStorageLocation switch which one. Set this in app startup BEFORE the first getDirectoryHandle.
393
375
  let opfsEnabled = false;
394
376
  const OPFS_FOLDERS_DIR = "folders";
395
377
  const OPFS_DEFAULT_FOLDER = "default";
@@ -401,14 +383,12 @@ function setCurrentOpfsFolder(name: string): void {
401
383
  localStorage.setItem(opfsCurrentKey(), name);
402
384
  }
403
385
 
404
- // Switches getDirectoryHandle to the Origin Private File System. No directory picker, no permission
405
- // prompt, no remote server check. Persists nothing — call on every app start before reading storage.
386
+ // Switches getDirectoryHandle to the Origin Private File System. No directory picker, no permission prompt, no remote server check. Persists nothing — call on every app start before reading storage.
406
387
  export function usePrivateFileSystem(): void {
407
388
  opfsEnabled = true;
408
389
  }
409
390
 
410
- // Whether the OPFS branch is in effect (so other callers can adapt — e.g. skip the "find data subdir"
411
- // hack in getFileStorageNested2 since OPFS is always a clean root we own end-to-end).
391
+ // Whether the OPFS branch is in effect (so other callers can adapt — e.g. skip the "find data subdir" hack in getFileStorageNested2 since OPFS is always a clean root we own end-to-end).
412
392
  export function isPrivateFileSystemActive(): boolean {
413
393
  return opfsEnabled;
414
394
  }
@@ -431,9 +411,7 @@ async function getCurrentOpfsHandle(): Promise<DirectoryWrapper> {
431
411
  return folder as unknown as DirectoryWrapper;
432
412
  }
433
413
 
434
- // All previously-used OPFS subfolders under this fileAPIKey, sorted alphabetically (timestamp-named
435
- // auto-generated folders therefore sort chronologically). Use this to surface a list, then call
436
- // pickPrivateFolder(name) to switch.
414
+ // All previously-used OPFS subfolders under this fileAPIKey, sorted alphabetically (timestamp-named auto-generated folders therefore sort chronologically). Use this to surface a list, then call pickPrivateFolder(name) to switch.
437
415
  export async function listPrivateFolders(): Promise<string[]> {
438
416
  if (!navigator.storage?.getDirectory) return [];
439
417
  let folders: FileSystemDirectoryHandle;
@@ -445,17 +423,13 @@ export async function listPrivateFolders(): Promise<string[]> {
445
423
  return names.sort();
446
424
  }
447
425
 
448
- // Switch to a specific OPFS subfolder and reload so getDirectoryHandle re-resolves to it. The folder
449
- // is created if it doesn't exist yet.
426
+ // Switch to a specific OPFS subfolder and reload so getDirectoryHandle re-resolves to it. The folder is created if it doesn't exist yet.
450
427
  export function pickPrivateFolder(name: string): void {
451
428
  setCurrentOpfsFolder(name);
452
429
  window.location.reload();
453
430
  }
454
431
 
455
- // Returns the directory handle to use — local (Node / picked folder) OR a remote server, both as the
456
- // same DirectoryWrapper, so callers don't know or care which it is. A remembered server is VERIFIED
457
- // (we actually connect) before use; if it no longer works we re-prompt, just like a local folder whose
458
- // permission was lost. Blocks until ready (or the user dismisses, which rejects).
432
+ // Returns the directory handle to use — local (Node / picked folder) OR a remote server, both as the same DirectoryWrapper, so callers don't know or care which it is. A remembered server is VERIFIED (we actually connect) before use; if it no longer works we re-prompt, just like a local folder whose permission was lost. Blocks until ready (or the user dismisses, which rejects).
459
433
  export const getDirectoryHandle = lazy(async function getDirectoryHandle(): Promise<DirectoryWrapper> {
460
434
  // An injected root (e.g. a Web Worker handed the handle by its owning window)
461
435
  // short-circuits the whole interactive resolution — no picker, no DOM, no
@@ -463,12 +437,7 @@ export const getDirectoryHandle = lazy(async function getDirectoryHandle(): Prom
463
437
  if (storageRootOverride) {
464
438
  return storageRootOverride as unknown as DirectoryWrapper;
465
439
  }
466
- // A worker can't show the picker (no DOM / no user activation) and can't read localStorage, but
467
- // it CAN open the pointer IndexedDB the main-thread picker persists to. Poll it for a handle
468
- // whose permission is already granted; the owning window may also postMessage a handle in
469
- // mid-poll (checked each iteration). This has to come before the isNode branch: typesafecss's
470
- // isNode() returns true in workers (window is undefined), so a worker would otherwise fall into
471
- // the Node branch and try to use fs/path.
440
+ // A worker can't show the picker (no DOM / no user activation) and can't read localStorage, but it CAN open the pointer IndexedDB the main-thread picker persists to. Poll it for a handle whose permission is already granted; the owning window may also postMessage a handle in mid-poll (checked each iteration). This has to come before the isNode branch: typesafecss's isNode() returns true in workers (window is undefined), so a worker would otherwise fall into the Node branch and try to use fs/path.
472
441
  if ("WorkerGlobalScope" in globalThis) {
473
442
  while (true) {
474
443
  if (storageRootOverride) return storageRootOverride as unknown as DirectoryWrapper;
@@ -512,8 +481,7 @@ export const getDirectoryHandle = lazy(async function getDirectoryHandle(): Prom
512
481
  }
513
482
  };
514
483
  const onConnected = (config: RemoteConfig) => { saveRemoteConfig(config); resolveHandle(getRemoteHandle(config)); };
515
- // The three options, rendered fresh each time. If a saved server just failed, pre-fill + expand
516
- // the connect form so the user can retry or fix it.
484
+ // The three options, rendered fresh each time. If a saved server just failed, pre-fill + expand the connect form so the user can retry or fix it.
517
485
  const renderOptions = () => (
518
486
  <>
519
487
  <button className={css.fontSize(40).pad2(80, 40)} onClick={pickLocal}>Pick Data Directory</button>
@@ -523,8 +491,7 @@ export const getDirectoryHandle = lazy(async function getDirectoryHandle(): Prom
523
491
  </>
524
492
  );
525
493
 
526
- // A previously-picked local folder: try to restore it (may need a click). Skipped when a saved
527
- // server failed — that user wants the server, so go straight to the (pre-filled) prompt.
494
+ // A previously-picked local folder: try to restore it (may need a click). Skipped when a saved server failed — that user wants the server, so go straight to the (pre-filled) prompt.
528
495
  const storedId = !savedRemote && localStorage.getItem(getFileAPIKey());
529
496
  if (storedId) {
530
497
  let doneLoad = false;
@@ -582,8 +549,7 @@ export const getFileStorageNested2 = cache(async function getFileStorage(pathStr
582
549
  base = new NodeJSDirectoryHandleWrapper(path.resolve("./data/"));
583
550
  } else {
584
551
  base = await getDirectoryHandle();
585
- // Skip the "find data subdir" hack under OPFS — that's for picked directories where the user
586
- // might have selected a folder with unrelated files; OPFS is a clean root we own end-to-end.
552
+ // Skip the "find data subdir" hack under OPFS — that's for picked directories where the user might have selected a folder with unrelated files; OPFS is a clean root we own end-to-end.
587
553
  if (!opfsEnabled) {
588
554
  let dirs: string[] = [];
589
555
  let alls: string[] = [];
@@ -614,8 +580,7 @@ export const getFileStorage = lazy(async function getFileStorage(): Promise<File
614
580
  });
615
581
  export function resetStorageLocation() {
616
582
  if (opfsEnabled) {
617
- // Don't delete previous folder — listPrivateFolders / pickPrivateFolder still need it. Just point
618
- // "current" at a fresh timestamp-named one so the next reload starts clean.
583
+ // Don't delete previous folder — listPrivateFolders / pickPrivateFolder still need it. Just point "current" at a fresh timestamp-named one so the next reload starts clean.
619
584
  setCurrentOpfsFolder(new Date().toISOString().replace(/[:.]/g, "-"));
620
585
  window.location.reload();
621
586
  return;
@@ -634,8 +599,7 @@ export type NestedFileStorage = {
634
599
 
635
600
  export type FileStorage = IStorageRaw & {
636
601
  folder: NestedFileStorage;
637
- // Mirrors DirectoryWrapper.isRemote: true when this storage is served over the network. Lets callers
638
- // (e.g. BulkDatabase2, which skips automatic compaction over the network by default) adapt.
602
+ // Mirrors DirectoryWrapper.isRemote: true when this storage is served over the network. Lets callers (e.g. BulkDatabase2, which skips automatic compaction over the network by default) adapt.
639
603
  isRemote?: boolean;
640
604
  };
641
605
 
@@ -671,8 +635,7 @@ async function fixedGetFileHandle(config: {
671
635
  return await config.handle.getFileHandle(config.key, { create: true });
672
636
  }
673
637
 
674
- // A file that genuinely does not exist. We must NOT retry these, otherwise reading many missing
675
- // files (a common pattern) gets catastrophically slow.
638
+ // A file that genuinely does not exist. We must NOT retry these, otherwise reading many missing files (a common pattern) gets catastrophically slow.
676
639
  function isMissingError(error: unknown): boolean {
677
640
  let name = (error as { name?: string })?.name;
678
641
  let code = (error as { code?: string })?.code;
@@ -681,10 +644,7 @@ function isMissingError(error: unknown): boolean {
681
644
 
682
645
  const MAX_READ_RETRIES = 6;
683
646
 
684
- // The browser File System Access API can transiently fail reads under heavy load (it appears to
685
- // throttle when a page issues many reads at once). Those failures surface as errors OTHER than
686
- // "not found", so we retry them with backoff. A real missing file (NotFoundError / ENOENT) returns
687
- // undefined immediately with no retry.
647
+ // The browser File System Access API can transiently fail reads under heavy load (it appears to throttle when a page issues many reads at once). Those failures surface as errors OTHER than "not found", so we retry them with backoff. A real missing file (NotFoundError / ENOENT) returns undefined immediately with no retry.
688
648
  async function readWithRetry<T>(label: string, key: string, read: () => Promise<T>): Promise<T | undefined> {
689
649
  let backoff = 25;
690
650
  for (let attempt = 0; ; attempt++) {
@@ -724,8 +684,7 @@ function wrapHandleFiles(handle: DirectoryWrapper): IStorageRaw {
724
684
  const file = await handle.getFileHandle(key);
725
685
  const fileContent = await file.getFile();
726
686
  const arrayBuffer = await fileContent.arrayBuffer();
727
- // Under load the FS Access API can resolve with a truncated buffer and no error.
728
- // Treat a short read as transient so readWithRetry retries it.
687
+ // Under load the FS Access API can resolve with a truncated buffer and no error. Treat a short read as transient so readWithRetry retries it.
729
688
  if (arrayBuffer.byteLength !== fileContent.size) {
730
689
  throw Object.assign(new Error(`Short read: got ${arrayBuffer.byteLength} of ${fileContent.size} bytes`), { name: "ShortReadError" });
731
690
  }
@@ -749,8 +708,7 @@ function wrapHandleFiles(handle: DirectoryWrapper): IStorageRaw {
749
708
 
750
709
  async append(key: string, value: Buffer): Promise<void> {
751
710
  await appendQueue(key)(async () => {
752
- // NOTE: Interesting point. Chrome doesn't optimize this to be an append, and instead
753
- // rewrites the entire file.
711
+ // NOTE: Interesting point. Chrome doesn't optimize this to be an append, and instead rewrites the entire file.
754
712
  const file = await fixedGetFileHandle({ handle, key, create: true });
755
713
  const writable = await file.createWritable({ keepExistingData: true });
756
714
  let offset = (await file.getFile()).size;
@@ -837,10 +795,7 @@ export function wrapHandle(handle: DirectoryWrapper): FileStorage {
837
795
  };
838
796
  }
839
797
 
840
- // Returns a URL for a file's bytes, ready to drop into <video>/<img>/fetch. A native (local) file becomes
841
- // an in-memory blob: URL; a remote file becomes an https URL into the server's range-capable /media
842
- // endpoint (auth token in the query, since a media element can't send headers). Both support HTTP range /
843
- // seeking. ALWAYS hand the result to disposeFileURL when finished — for blob: URLs that frees memory.
798
+ // Returns a URL for a file's bytes, ready to drop into <video>/<img>/fetch. A native (local) file becomes an in-memory blob: URL; a remote file becomes an https URL into the server's range-capable /media endpoint (auth token in the query, since a media element can't send headers). Both support HTTP range / seeking. ALWAYS hand the result to disposeFileURL when finished — for blob: URLs that frees memory.
844
799
  export async function getFileURL(file: FileWrapper): Promise<string> {
845
800
  if (file.getURL) return file.getURL();
846
801
  // Native FileSystemFileHandle (or any Blob-backed file): a blob: URL over the File itself.
@@ -848,16 +803,14 @@ export async function getFileURL(file: FileWrapper): Promise<string> {
848
803
  return URL.createObjectURL(f as unknown as Blob);
849
804
  }
850
805
 
851
- // Releases a URL from getFileURL. blob: URLs leak until the document is gone unless revoked; https/file:
852
- // URLs need no cleanup, so this is a no-op for them.
806
+ // Releases a URL from getFileURL. blob: URLs leak until the document is gone unless revoked; https/file: URLs need no cleanup, so this is a no-op for them.
853
807
  export function disposeFileURL(url: string): void {
854
808
  if (url.startsWith("blob:")) {
855
809
  try { URL.revokeObjectURL(url); } catch { /* not in a browser, or already revoked */ }
856
810
  }
857
811
  }
858
812
 
859
- // A StorageFactory backed by a remote server (path -> FileStorage), for code that injects its own
860
- // storage into BulkDatabase2 rather than going through the directory prompt.
813
+ // A StorageFactory backed by a remote server (path -> FileStorage), for code that injects its own storage into BulkDatabase2 rather than going through the directory prompt.
861
814
  export function getRemoteFileStorageFactory(url: string, password: string, options?: RemoteOptions): (pathStr: string) => Promise<FileStorage> {
862
815
  const root = getRemoteDirectoryHandle(url, password, options);
863
816
  return async (pathStr: string) => {
@@ -28,11 +28,12 @@ export type CommonConfig = {
28
28
  validWindow: [number, number];
29
29
  /** Sharding: the fraction of the key space this source handles, as [start, end) over [0, 1) (keys are routed by getRoute in remoteConfig.ts). Defaults to FULL_ROUTE (unsharded). At every point in time the sources' routes must fully cover [0, 1), or some keys could never be read. */
30
30
  route?: [number, number];
31
+ /** Set on entries injected into the in-memory config by an overlay (a deploy switchover's alternate-port window). Never written to disk: resolveIntermediateSources strips these and rejoins the windows around them, which is also how a client tells whether an update is a real configuration change or just an overlay. */
32
+ intermediate?: boolean;
31
33
  };
32
34
  export type HostedConfig = CommonConfig & {
33
35
  type: "remote";
34
36
  url: string;
35
- accountName?: string;
36
37
  public?: boolean;
37
38
  fast?: boolean;
38
39
  writeDelay?: number;
@@ -104,8 +105,7 @@ export type ArchivesSyncStatus = {
104
105
  };
105
106
  export interface IArchives {
106
107
  getDebugName(): string;
107
- /** Whether writes would be accepted (credentials exist, the account trusts this machine, etc).
108
- * Checked without writing anything. */
108
+ /** Whether writes would be accepted (credentials exist, the account trusts this machine, etc). Checked without writing anything. */
109
109
  hasWriteAccess(): Promise<boolean>;
110
110
  get(fileName: string, config?: {
111
111
  range?: {
@@ -1,14 +1,11 @@
1
1
  module.allowclient = true;
2
2
 
3
- // The important operations of an archive bucket (extracted from ArchivesBackblaze), so other
4
- // backends (e.g. our own remote storage server) can be used interchangeably.
3
+ // The important operations of an archive bucket (extracted from ArchivesBackblaze), so other backends (e.g. our own remote storage server) can be used interchangeably.
5
4
 
6
- // A write may not be stamped more than this far in the future, or clock skew between machines
7
- // would let a bad timestamp block writes for a long time.
5
+ // A write may not be stamped more than this far in the future, or clock skew between machines would let a bad timestamp block writes for a long time.
8
6
  export const MAX_LAST_MODIFIED_FUTURE = 15 * 60 * 1000;
9
7
 
10
- // How long browsers may cache files from immutable buckets (the Cache-Control max-age), shared by
11
- // every hosting path (backblaze bucket settings and our own storage server's HTTP route)
8
+ // How long browsers may cache files from immutable buckets (the Cache-Control max-age), shared by every hosting path (backblaze bucket settings and our own storage server's HTTP route)
12
9
  export const IMMUTABLE_CACHE_TIME = 86400 * 1000;
13
10
  export function assertValidLastModified(lastModified: number): void {
14
11
  let max = Date.now() + MAX_LAST_MODIFIED_FUTURE;
@@ -46,6 +43,8 @@ export type CommonConfig = {
46
43
  validWindow: [number, number];
47
44
  /** Sharding: the fraction of the key space this source handles, as [start, end) over [0, 1) (keys are routed by getRoute in remoteConfig.ts). Defaults to FULL_ROUTE (unsharded). At every point in time the sources' routes must fully cover [0, 1), or some keys could never be read. */
48
45
  route?: [number, number];
46
+ /** Set on entries injected into the in-memory config by an overlay (a deploy switchover's alternate-port window). Never written to disk: resolveIntermediateSources strips these and rejoins the windows around them, which is also how a client tells whether an update is a real configuration change or just an overlay. */
47
+ intermediate?: boolean;
49
48
  };
50
49
 
51
50
  export type HostedConfig = CommonConfig & {
@@ -56,16 +55,12 @@ export type HostedConfig = CommonConfig & {
56
55
  url: string;
57
56
 
58
57
  // NOTE: Authentication is handled by cert.ts, via having your machine trusted to access this account.
59
- accountName?: string;
60
58
 
61
59
  public?: boolean;
62
- // Fast mode: the server acknowledges writes once they are in memory, flushing to disk after
63
- // writeDelay (default 5 minutes) and coalescing writes to the same file. A server crash loses
64
- // writes that haven't flushed yet.
60
+ // Fast mode: the server acknowledges writes once they are in memory, flushing to disk after writeDelay (default 5 minutes) and coalescing writes to the same file. A server crash loses writes that haven't flushed yet.
65
61
  fast?: boolean;
66
62
  writeDelay?: number;
67
- // The bucket is served straight from the server's disk, with no index — so no fast writes and
68
- // no getChangesAfter/getSyncStatus.
63
+ // The bucket is served straight from the server's disk, with no index — so no fast writes and no getChangesAfter/getSyncStatus.
69
64
  rawDisk?: boolean;
70
65
  // Writes to paths that already exist are disallowed (deletes still work).
71
66
  immutable?: boolean;
@@ -81,9 +76,7 @@ export type BackblazeConfig = CommonConfig & {
81
76
  // NOTE: This isn't enforced on the backblaze level, so this is just a client-side guarantee. This can change how we cache files.
82
77
  // - Backblaze does support immutability. However, apparently, once we enable it on a bucket, we can't disable it, which is really bad, as it means if our code could ever enable it and we accidentally enable it on an important bucket, we essentially just bricked that bucket. So we should never write any code that ever tries to use backblaze to make things immutable.
83
78
  immutable?: boolean;
84
- // CORS origins allowed to consume the bucket's files in a browser. Not a security boundary
85
- // (access is gated by the API key / signed URLs, neither of which rides in cookies) - it only
86
- // controls which sites' in-page JavaScript can read responses. Defaults to any HTTPS origin.
79
+ // CORS origins allowed to consume the bucket's files in a browser. Not a security boundary (access is gated by the API key / signed URLs, neither of which rides in cookies) - it only controls which sites' in-page JavaScript can read responses. Defaults to any HTTPS origin.
87
80
  allowedOrigins?: string[];
88
81
 
89
82
  // NOTE: We will access the api key from getSecret, see backblaze.ts for the specific keys.
@@ -95,16 +88,12 @@ export const FULL_VALID_WINDOW: [number, number] = [0, Number.MAX_SAFE_INTEGER];
95
88
 
96
89
 
97
90
 
98
- // createTime is a misnomer kept for compatibility — it is really the LAST-WRITE time, same as
99
- // getInfo's writeTime. Neither Backblaze nor our remote storage tracks a distinct creation date:
100
- // each write stamps a fresh timestamp on the current version, so both fields are just "when the
101
- // bytes served by get() were most recently written".
91
+ // createTime is a misnomer kept for compatibility — it is really the LAST-WRITE time, same as getInfo's writeTime. Neither Backblaze nor our remote storage tracks a distinct creation date: each write stamps a fresh timestamp on the current version, so both fields are just "when the bytes served by get() were most recently written".
102
92
  export type ArchiveFileInfo = { path: string; createTime: number; size: number };
103
93
 
104
94
  // An in-progress background synchronization task (see ArchivesConfig.syncing)
105
95
  export type SyncActivity = {
106
- // A metadata scan is a single listing call, so it has no incremental progress - just that it's
107
- // running and since when. A full sync knows its exact file/byte progress.
96
+ // A metadata scan is a single listing call, so it has no incremental progress - just that it's running and since when. A full sync knows its exact file/byte progress.
108
97
  type: "metadataScan" | "fullSync";
109
98
  sourceDebugName: string;
110
99
  startTime: number;
@@ -117,14 +106,11 @@ export type SyncActivity = {
117
106
  export type ArchivesConfig = {
118
107
  // Whether getChangesAfter is implemented (fast change polling, instead of full rescans)
119
108
  supportsChangesAfter?: boolean;
120
- // The bucket's full routing config (ROUTING_FILE). Absent for sources that don't have one (a
121
- // bare disk source, or a bucket that doesn't exist yet).
109
+ // The bucket's full routing config (ROUTING_FILE). Absent for sources that don't have one (a bare disk source, or a bucket that doesn't exist yet).
122
110
  remoteConfig?: RemoteConfig;
123
- // Live index totals (tombstones excluded), kept up to date in memory on every mutation and
124
- // recomputed on load - so any drift heals on restart
111
+ // Live index totals (tombstones excluded), kept up to date in memory on every mutation and recomputed on load - so any drift heals on restart
125
112
  index?: { fileCount: number; byteCount: number };
126
- // The same totals broken down by which source currently holds each file's bytes (the first
127
- // entry is the server's own disk)
113
+ // The same totals broken down by which source currently holds each file's bytes (the first entry is the server's own disk)
128
114
  indexSources?: { debugName: string; fileCount: number; byteCount: number }[];
129
115
  // The server's configured readerDiskLimit, when it runs as a bounded read cache
130
116
  readerDiskLimit?: number;
@@ -135,41 +121,27 @@ export type ArchivesConfig = {
135
121
  // A synchronization source of a BlobStore (which synchronizes an index + local cache from them)
136
122
  export type ArchivesSource = {
137
123
  source: IArchives;
138
- // From the source's CommonConfig. Values with write times outside the window are ignored when
139
- // scanning, and once its end is past (see windowAcceptsWrites) the source stops
140
- // receiving writes entirely - still scanned (it holds the authoritative data for its window),
141
- // it just stops growing.
124
+ // From the source's CommonConfig. Values with write times outside the window are ignored when scanning, and once its end is past (see windowAcceptsWrites) the source stops receiving writes entirely - still scanned (it holds the authoritative data for its window), it just stops growing.
142
125
  validWindow: [number, number];
143
- // From the source's CommonConfig (intersected with the owning store's own route): only keys
144
- // routing into [start, end) are accepted from this source's scans and sent to it in
145
- // writes/reconciliation. The routing file is exempt - config flows everywhere. Absent = all keys.
126
+ // From the source's CommonConfig (intersected with the owning store's own route): only keys routing into [start, end) are accepted from this source's scans and sent to it in writes/reconciliation. The routing file is exempt - config flows everywhere. Absent = all keys.
146
127
  route?: [number, number];
147
128
  // From the source's CommonConfig; see there.
148
129
  noFullSync?: boolean;
149
- // Stable identity of the underlying endpoint (its config with windows/routes stripped) - how
150
- // BlobStore.updateSources recognizes a source across config changes so it can update it in
151
- // place instead of removing and re-adding it
130
+ // Stable identity of the underlying endpoint (its config with windows/routes stripped) - how BlobStore.updateSources recognizes a source across config changes so it can update it in place instead of removing and re-adding it
152
131
  identity?: string;
153
132
  };
154
133
 
155
- // Error marker a server includes when a freshly-stamped write reaches it outside its valid windows
156
- // (the client resolved its target, then time crossed a window boundary before the write landed).
157
- // Clients detect this marker and re-resolve the currently-valid source, retrying ONCE - boundaries
158
- // are far apart, so hitting it twice in one attempt means something is actually wrong.
134
+ // Error marker a server includes when a freshly-stamped write reaches it outside its valid windows (the client resolved its target, then time crossed a window boundary before the write landed). Clients detect this marker and re-resolve the currently-valid source, retrying ONCE - boundaries are far apart, so hitting it twice in one attempt means something is actually wrong.
159
135
  export const STORAGE_WRONG_VALID_WINDOW = "REMOTE_STORAGE_WRONG_VALID_WINDOW_a7c1f04e";
160
136
 
161
- // Error marker a server includes when a freshly-stamped write's key routes outside the shards this
162
- // server handles (the client's config disagrees with the server's - clients re-resolve once)
137
+ // Error marker a server includes when a freshly-stamped write's key routes outside the shards this server handles (the client's config disagrees with the server's - clients re-resolve once)
163
138
  export const STORAGE_WRONG_ROUTE = "REMOTE_STORAGE_WRONG_ROUTE_c94d2e17";
164
139
 
165
140
  export const FULL_ROUTE: [number, number] = [0, 1];
166
141
 
167
- // A key containing this sentinel doesn't have a fixed shard: setVariableShard picks the (lowest
168
- // latency, up) write shard, appends "_<value in the shard's route>" directly after the sentinel,
169
- // and returns the materialized key. getRoute treats that suffix as a complete route override.
142
+ // A key containing this sentinel doesn't have a fixed shard: setVariableShard picks the (lowest latency, up) write shard, appends "_<value in the shard's route>" directly after the sentinel, and returns the materialized key. getRoute treats that suffix as a complete route override.
170
143
  export const VARIABLE_SHARD = "VARIABLE_SHARD_f0234jfah08fgyhfgyssdds83nmp";
171
- // No grace past the end: a window boundary is a hard handoff (clients retry a rejected write
172
- // against the newly-valid source, so leniency here would only desynchronize the handoff)
144
+ // No grace past the end: a window boundary is a hard handoff (clients retry a rejected write against the newly-valid source, so leniency here would only desynchronize the handoff)
173
145
  export function windowAcceptsWrites(validWindow: [number, number] | undefined): boolean {
174
146
  if (!validWindow) return true;
175
147
  return validWindow[1] > Date.now();
@@ -194,8 +166,7 @@ export type ArchivesSyncStatus = {
194
166
 
195
167
  export interface IArchives {
196
168
  getDebugName(): string;
197
- /** Whether writes would be accepted (credentials exist, the account trusts this machine, etc).
198
- * Checked without writing anything. */
169
+ /** Whether writes would be accepted (credentials exist, the account trusts this machine, etc). Checked without writing anything. */
199
170
  hasWriteAccess(): Promise<boolean>;
200
171
  get(fileName: string, config?: { range?: { start: number; end: number } }): Promise<Buffer | undefined>;
201
172
  get2(fileName: string, config?: { range?: { start: number; end: number } }): Promise<{ data: Buffer; writeTime: number; size: number } | undefined>;
@@ -25,12 +25,10 @@ export type IStorage<T> = {
25
25
  // Allows watching for when the storage detects and underlying changes, and resyncs all of it's data (which might
26
26
  watchResync?: (callback: () => void) => void;
27
27
  };
28
- // NOTE: In the file system some characters are disallowed, and some characters do special things
29
- // (/ makes a folder). And there are even more rules, such as lengths per folder, etc, etc.
28
+ // NOTE: In the file system some characters are disallowed, and some characters do special things (/ makes a folder). And there are even more rules, such as lengths per folder, etc, etc.
30
29
  export type IStorageRaw = {
31
30
  get(key: string): Promise<Buffer | undefined>;
32
- // Reads bytes in the range [start, end) (end is exclusive, clamped to the file size).
33
- // Returns undefined if the file doesn't exist.
31
+ // Reads bytes in the range [start, end) (end is exclusive, clamped to the file size). Returns undefined if the file doesn't exist.
34
32
  getRange(key: string, config: { start: number; end: number }): Promise<Buffer | undefined>;
35
33
  // May or may not be efficient in the underlying storage
36
34
  append(key: string, value: Buffer): Promise<void>;
@@ -22,8 +22,7 @@ export function hasPending(): boolean {
22
22
  return Object.keys(watchState.pending).length > 0;
23
23
  }
24
24
 
25
- // NOTE: This not only prevents render overload, but also means any pending that are < this
26
- // delay don't show up (which is useful to reduce unnecessary pending messages).
25
+ // NOTE: This not only prevents render overload, but also means any pending that are < this delay don't show up (which is useful to reduce unnecessary pending messages).
27
26
  const setPendingBase = throttleFunction(500, function setPendingBase() {
28
27
  for (let [group, message] of pendingCache) {
29
28
 
@@ -38,7 +37,7 @@ const setPendingBase = throttleFunction(500, function setPendingBase() {
38
37
  }
39
38
  delete watchState.pending[group];
40
39
  } else {
41
- //console.log("setPending", group, message);
40
+ // console.log("setPending", group, message);
42
41
  if (!(group in watchState.pending)) {
43
42
  pendingLastSets.set(group, Date.now());
44
43
  }
@@ -13,27 +13,20 @@ import { runInfinitePoll } from "socket-function/src/batching";
13
13
  // - Zip individual large values
14
14
  // - Stores a transaction log
15
15
  // - Transaction log has a header, which is JSON, for things such as "zipped"
16
- // - Transaction log uses length prefixed values, with a special 8 bytes to denote the end,
17
- // and 8 for the start.
18
- // - When transaction log is iterated on, if the bytes at the end (using the length prefix)
19
- // don't match the end bytes OR the start bytes are wrong, we skip the start bytes,
20
- // and iterating until we find new start bytes that match our special bytes. Then we try
21
- // to read these values.
16
+ // - Transaction log uses length prefixed values, with a special 8 bytes to denote the end, and 8 for the start.
17
+ // - When transaction log is iterated on, if the bytes at the end (using the length prefix) don't match the end bytes OR the start bytes are wrong, we skip the start bytes, and iterating until we find new start bytes that match our special bytes. Then we try to read these values.
22
18
  // - Each transaction entry has a byte for flags, one bit which denotes if the value is zipped or not.
23
19
  // - Compresses the log after there are 3X entries than keys (and > 100)
24
20
  // - Both dedupe keys, and zip
25
21
  // - Assumes all files in rawStorage ending with .tx are transaction logs
26
22
  // - Names files like `${generation}.tx`
27
- // - When compressing, we increment the generation, and write to a new file, and delete
28
- // any generations that are older than the new one
23
+ // - When compressing, we increment the generation, and write to a new file, and delete any generations that are older than the new one
29
24
  // - On reading, we read from all generation files that exist, in case some are corrupted
30
25
  // - On load, loads in all transaction logs, and stores all values in a Map<string, Buffer>
31
26
  // - On writes immediately updates the in memory Map, and then writes to the transaction log
32
27
  // - Caches the last transaction file name in memory
33
- // - Makes sure all file system writes (but not Map updates) are done with fileLockSection,
34
- // so they never overlap.
35
- // - Buffers pending appends in memory, so they can written all at once (after the first one
36
- // is blocking in fileLockSection).
28
+ // - Makes sure all file system writes (but not Map updates) are done with fileLockSection, so they never overlap.
29
+ // - Buffers pending appends in memory, so they can written all at once (after the first one is blocking in fileLockSection).
37
30
 
38
31
  UPDATE now we use chunks, because append is too slow.
39
32
 
@@ -93,9 +86,7 @@ export class TransactionStorage implements IStorage<Buffer> {
93
86
  private rawStorage: IStorageRaw,
94
87
  private debugName: string,
95
88
  private writeDelay = WRITE_DELAY,
96
- // When set, we poll the underlying storage and reload if another process/tab writes new
97
- // chunk files. Off by default, as it costs a periodic disk scan and most collections are
98
- // only written by a single process.
89
+ // When set, we poll the underlying storage and reload if another process/tab writes new chunk files. Off by default, as it costs a periodic disk scan and most collections are only written by a single process.
99
90
  private resyncFromDisk = false
100
91
  ) {
101
92
  TransactionStorage.allStorage.push(this);
@@ -241,7 +232,7 @@ export class TransactionStorage implements IStorage<Buffer> {
241
232
  private updatePendingAppends = throttleFunction(100, async () => {
242
233
  let appendCount = this.pendingAppends.length + this.extraAppends;
243
234
  let group = `Transaction (${this.debugName})`;
244
- //console.log(`Update pending appends ${group}: ${appendCount}`);
235
+ // console.log(`Update pending appends ${group}: ${appendCount}`);
245
236
  if (!appendCount) {
246
237
  setPending(group, "");
247
238
  return;
@@ -322,7 +313,7 @@ export class TransactionStorage implements IStorage<Buffer> {
322
313
  const fullFile = await this.rawStorage.get(filename);
323
314
  if (!fullFile) return [];
324
315
  if (fullFile.length < 4) {
325
- //console.error(`Transaction in ${this.debugName} file ${filename} is too small, skipping`);
316
+ // console.error(`Transaction in ${this.debugName} file ${filename} is too small, skipping`);
326
317
  return [];
327
318
  }
328
319
  size.value += fullFile.length;
@@ -549,8 +540,7 @@ export class TransactionStorage implements IStorage<Buffer> {
549
540
  existingDiskEntries = existingDiskEntries.filter(x => x.endsWith(CHUNK_EXT));
550
541
  let compressNow = force || (
551
542
  this.entryCount > 100 && this.entryCount > this.cache.size * 3
552
- // NOTE: This compress check breaks down if we only have very large values, but... those
553
- // don't work ANYWAYS (it is better to use one file per value instead).
543
+ // NOTE: This compress check breaks down if we only have very large values, but... those don't work ANYWAYS (it is better to use one file per value instead).
554
544
  // - Maybe we should throw, or at least warn, on sets of value > 1MB,
555
545
  // at which point they should just use a file per value
556
546
  || existingDiskEntries.length > Math.max(10, Math.ceil(this.entryCount / 1000))
@@ -559,8 +549,7 @@ export class TransactionStorage implements IStorage<Buffer> {
559
549
  if (!compressNow) return;
560
550
  console.log(`Compressing ${this.debugName} transaction log, ${this.entryCount} entries, ${this.cache.size} keys`);
561
551
 
562
- // Load off disk, in case there are other writes. We still race with them, but at least
563
- // this reduces the race condition considerably
552
+ // Load off disk, in case there are other writes. We still race with them, but at least this reduces the race condition considerably
564
553
  existingDiskEntries = await this.loadAllTransactions();
565
554
 
566
555
  this.entryCount = this.cache.size;
@@ -598,9 +587,7 @@ export class TransactionStorage implements IStorage<Buffer> {
598
587
  }
599
588
  }
600
589
 
601
- // This is the ONLY time we can delete old files, as we know for sure the new file has all of our data.
602
- // Any future readers won't know this, unless they write it themselves (or unless they audit it against
603
- // the other generations, which is annoying).
590
+ // This is the ONLY time we can delete old files, as we know for sure the new file has all of our data. Any future readers won't know this, unless they write it themselves (or unless they audit it against the other generations, which is annoying).
604
591
  for (const file of existingDiskEntries) {
605
592
  await this.rawStorage.remove(file);
606
593
  this.diskFiles.delete(file);