sliftutils 1.7.140 → 1.7.142

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.
@@ -3,7 +3,7 @@ import { delay } from "socket-function/src/batching";
3
3
  import {
4
4
  IArchives, RemoteConfig, RemoteConfigBase, SourceConfig,
5
5
  ArchiveFileInfo, ArchivesConfig, ArchivesSyncStatus, ChangesAfterConfig, DelConfig, FindConfig, GetConfig, GetInfoConfig, MoveFileConfig, SetConfig, SetLargeFileConfig, STORAGE_WRONG_VALID_WINDOW,
6
- STORAGE_WRONG_ROUTE, STORAGE_NOT_CONFIGURED, FULL_ROUTE, VARIABLE_SHARD, LARGE_SET_THRESHOLD, bufferChunkStream,
6
+ STORAGE_WRONG_ROUTE, STORAGE_NOT_CONFIGURED, FULL_ROUTE, VARIABLE_SHARD, LARGE_SET_THRESHOLD, bufferChunkStream, validateFileName,
7
7
  } from "../IArchives";
8
8
  import { copyArchiveFile } from "../archiveHelpers";
9
9
  import {
@@ -350,6 +350,7 @@ export class ArchivesChain implements IArchives {
350
350
  }
351
351
  /** get2, but trying sources in latency order (fastest first) instead of config order. While this is much faster, it might miss immediate writes: the write node is no longer tried first, so a lagging replica may answer with a slightly older value. Exclusive with noFallbacks (which only considers one source - the write node - so there is no order to speed up); passing both throws. */
352
352
  public async getFast(fileName: string, config?: GetConfig): Promise<{ data: Buffer; writeTime: number; size: number; url: string } | { data?: undefined; writeTime?: undefined; size?: undefined; url: string }> {
353
+ validateFileName(fileName, "getFast");
353
354
  if (config?.sourceUrl) {
354
355
  // A specific source leaves nothing for the latency ordering to decide
355
356
  return await this.get2(fileName, config);
@@ -363,6 +364,7 @@ export class ArchivesChain implements IArchives {
363
364
  }
364
365
  /** Always resolves with a url - the authority that answered. A value that doesn't exist is still an answer FROM a server, so it comes back as { url } with no data (never plain undefined); errors from every source throw instead. */
365
366
  public async get2(fileName: string, config?: GetConfig): Promise<{ data: Buffer; writeTime: number; size: number; url: string } | { data?: undefined; writeTime?: undefined; size?: undefined; url: string }> {
367
+ validateFileName(fileName, "get2");
366
368
  const sourceUrl = config?.sourceUrl;
367
369
  if (sourceUrl) {
368
370
  return await this.runOnSource(sourceUrl, async archives => {
@@ -379,6 +381,7 @@ export class ArchivesChain implements IArchives {
379
381
  });
380
382
  }
381
383
  public async getInfo(fileName: string, config?: GetInfoConfig): Promise<{ writeTime: number; size: number; url: string } | undefined> {
384
+ validateFileName(fileName, "getInfo");
382
385
  const sourceUrl = config?.sourceUrl;
383
386
  if (sourceUrl) {
384
387
  return await this.runOnSource(sourceUrl, async archives => {
@@ -549,6 +552,7 @@ export class ArchivesChain implements IArchives {
549
552
  }
550
553
 
551
554
  public async set(fileName: string, data: Buffer, config?: SetConfig): Promise<string> {
555
+ validateFileName(fileName, "set");
552
556
  if (!data.length) {
553
557
  throw new Error(`Empty write refused: set was called with an empty buffer for ${JSON.stringify(fileName)}: an empty file IS a deletion in this system and would read back as missing - call del instead`);
554
558
  }
@@ -605,11 +609,13 @@ export class ArchivesChain implements IArchives {
605
609
  return ROUTING_FILE;
606
610
  }
607
611
  public async del(fileName: string, config?: DelConfig): Promise<void> {
612
+ validateFileName(fileName, "del");
608
613
  await this.request({ fallbacks: !!config?.fallbacks, write: true, retries: config?.retries, route: getRoute(fileName), timeout: { uploadBytes: 0, label: `Deletion of ${JSON.stringify(fileName)}` } }, archives => archives.del(fileName, config));
609
614
  }
610
615
 
611
616
  /** See IArchives.undelete: restores a file marked for deletion, dispatched to the write node as SetConfig.undelete (the write node propagates the restore to its peers itself). */
612
617
  public async undelete(fileName: string): Promise<void> {
618
+ validateFileName(fileName, "undelete");
613
619
  // set refuses empty buffers, and an undelete carries no data - the byte is ignored
614
620
  let placeholder = Buffer.from([1]);
615
621
  await this.request({ fallbacks: false, write: true, route: getRoute(fileName), timeout: { uploadBytes: placeholder.length, label: `Undelete of ${JSON.stringify(fileName)}` } }, archives => archives.set(fileName, placeholder, { undelete: true }));
@@ -727,6 +733,7 @@ export class ArchivesChain implements IArchives {
727
733
 
728
734
  /** A large file is written exactly like a small one - same write node, same wrong-window/route re-resolution, same fallbacks - so a value's SIZE never decides its write semantics (set streams through here past LARGE_SET_THRESHOLD, and a file that grew past it must not suddenly lose the availability its caller asked for). The one difference: every attempt after the first has to rewind the stream, so a config without restartStream gets a single attempt. */
729
735
  public async setLargeFile(config: SetLargeFileConfig): Promise<void> {
736
+ validateFileName(config.path, "setLargeFile");
730
737
  if (config.path.includes(VARIABLE_SHARD) && parseVariableRoute(config.path) === undefined) {
731
738
  throw new Error(`setLargeFile does not support VARIABLE_SHARD keys (there is no way to return the materialized key); write the file with set, or materialize the key yourself. Key: ${JSON.stringify(config.path)}`);
732
739
  }