sliftutils 1.7.71 → 1.7.72

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 (91) hide show
  1. package/dist/treeSummary.ts.cache +2 -2
  2. package/index.d.ts +505 -257
  3. package/misc/dist/environment.ts.cache +2 -2
  4. package/misc/dist/getSecret.ts.cache +2 -2
  5. package/misc/dist/strings.ts.cache +2 -2
  6. package/misc/dist/zip.ts.cache +2 -2
  7. package/misc/https/dist/certs.ts.cache +2 -2
  8. package/misc/https/dist/hostServer.ts.cache +5 -2
  9. package/misc/https/dist/persistentLocalStorage.ts.cache +2 -2
  10. package/package.json +1 -1
  11. package/render-utils/dist/observer.tsx.cache +2 -2
  12. package/spec.txt +10 -0
  13. package/storage/ArchivesDisk.d.ts +4 -13
  14. package/storage/ArchivesDisk.ts +6 -4
  15. package/storage/BulkDatabase2/dist/BulkDatabaseBase.ts.cache +2 -2
  16. package/storage/BulkDatabase2/dist/BulkDatabaseFormat.ts.cache +2 -2
  17. package/storage/BulkDatabase2/dist/BulkDatabaseMerge.ts.cache +2 -2
  18. package/storage/BulkDatabase2/dist/BulkDatabaseReader.ts.cache +2 -2
  19. package/storage/BulkDatabase2/dist/LoadedIndex.ts.cache +2 -2
  20. package/storage/BulkDatabase2/dist/WriteOverlay.ts.cache +2 -2
  21. package/storage/BulkDatabase2/dist/blockCache.ts.cache +2 -2
  22. package/storage/BulkDatabase2/dist/mergeLock.ts.cache +2 -2
  23. package/storage/BulkDatabase2/dist/mergeMarkers.ts.cache +2 -2
  24. package/storage/BulkDatabase2/dist/streamLog.ts.cache +2 -2
  25. package/storage/BulkDatabase2/dist/syncClient.ts.cache +2 -2
  26. package/storage/IArchives.d.ts +14 -10
  27. package/storage/IArchives.ts +16 -4
  28. package/storage/backblaze.d.ts +3 -9
  29. package/storage/backblaze.ts +22 -19
  30. package/storage/dist/ArchivesDisk.ts.cache +6 -3
  31. package/storage/dist/FileFolderAPI.tsx.cache +2 -2
  32. package/storage/dist/IArchives.ts.cache +18 -4
  33. package/storage/dist/IndexedDBFileFolderAPI.ts.cache +2 -2
  34. package/storage/dist/JSONStorage.ts.cache +2 -2
  35. package/storage/dist/PendingManager.tsx.cache +2 -2
  36. package/storage/dist/TransactionStorage.ts.cache +2 -2
  37. package/storage/dist/backblaze.ts.cache +36 -27
  38. package/storage/dist/fileSystemPointer.ts.cache +2 -2
  39. package/storage/dist/remoteFileStorage.ts.cache +2 -2
  40. package/storage/remoteStorage/ArchivesRemote.d.ts +5 -9
  41. package/storage/remoteStorage/ArchivesRemote.ts +25 -20
  42. package/storage/remoteStorage/ArchivesUrl.d.ts +3 -9
  43. package/storage/remoteStorage/ArchivesUrl.ts +7 -5
  44. package/storage/remoteStorage/accessPage.tsx +4 -4
  45. package/storage/remoteStorage/accessStats.d.ts +2 -0
  46. package/storage/remoteStorage/accessStats.ts +32 -0
  47. package/storage/remoteStorage/blobStore.d.ts +193 -88
  48. package/storage/remoteStorage/blobStore.ts +535 -400
  49. package/storage/remoteStorage/bucketDisk.d.ts +18 -0
  50. package/storage/remoteStorage/bucketDisk.ts +66 -0
  51. package/storage/remoteStorage/createArchives.d.ts +4 -10
  52. package/storage/remoteStorage/createArchives.ts +101 -62
  53. package/storage/remoteStorage/dist/ArchivesRemote.ts.cache +29 -20
  54. package/storage/remoteStorage/dist/ArchivesUrl.ts.cache +8 -5
  55. package/storage/remoteStorage/dist/accessPage.tsx.cache +6 -6
  56. package/storage/remoteStorage/dist/accessStats.ts.cache +39 -4
  57. package/storage/remoteStorage/dist/blobStore.ts.cache +517 -375
  58. package/storage/remoteStorage/dist/bucketDisk.ts.cache +72 -0
  59. package/storage/remoteStorage/dist/cliArgs.ts.cache +2 -2
  60. package/storage/remoteStorage/dist/createArchives.ts.cache +112 -58
  61. package/storage/remoteStorage/dist/deployTakeover.ts.cache +2 -2
  62. package/storage/remoteStorage/dist/intermediateSources.ts.cache +13 -13
  63. package/storage/remoteStorage/dist/remoteConfig.ts.cache +19 -3
  64. package/storage/remoteStorage/dist/serverConfig.ts.cache +116 -0
  65. package/storage/remoteStorage/dist/sourceWrapper.ts.cache +7 -6
  66. package/storage/remoteStorage/dist/sourcesList.ts.cache +6 -3
  67. package/storage/remoteStorage/dist/storageClientController.ts.cache +2 -2
  68. package/storage/remoteStorage/dist/storageController.ts.cache +224 -191
  69. package/storage/remoteStorage/dist/storageServer.ts.cache +9 -9
  70. package/storage/remoteStorage/dist/storageServerState.ts.cache +330 -567
  71. package/storage/remoteStorage/dist/storePlan.ts.cache +149 -0
  72. package/storage/remoteStorage/dist/validation.ts.cache +40 -0
  73. package/storage/remoteStorage/grantAccessCli.ts +1 -1
  74. package/storage/remoteStorage/intermediateSources.d.ts +3 -5
  75. package/storage/remoteStorage/intermediateSources.ts +14 -16
  76. package/storage/remoteStorage/remoteConfig.d.ts +2 -2
  77. package/storage/remoteStorage/remoteConfig.ts +19 -3
  78. package/storage/remoteStorage/serverConfig.d.ts +27 -0
  79. package/storage/remoteStorage/serverConfig.ts +109 -0
  80. package/storage/remoteStorage/sourceWrapper.d.ts +3 -3
  81. package/storage/remoteStorage/sourceWrapper.ts +7 -6
  82. package/storage/remoteStorage/sourcesList.ts +3 -0
  83. package/storage/remoteStorage/storageController.d.ts +118 -36
  84. package/storage/remoteStorage/storageController.ts +166 -168
  85. package/storage/remoteStorage/storageServer.ts +1 -1
  86. package/storage/remoteStorage/storageServerState.d.ts +52 -63
  87. package/storage/remoteStorage/storageServerState.ts +340 -584
  88. package/storage/remoteStorage/storePlan.d.ts +34 -0
  89. package/storage/remoteStorage/storePlan.ts +166 -0
  90. package/storage/remoteStorage/validation.d.ts +4 -0
  91. package/storage/remoteStorage/validation.ts +31 -0
package/index.d.ts CHANGED
@@ -858,7 +858,7 @@ declare module "sliftutils/render-utils/observer" {
858
858
  declare module "sliftutils/storage/ArchivesDisk" {
859
859
  /// <reference types="node" />
860
860
  /// <reference types="node" />
861
- import { IArchives, ArchiveFileInfo, ArchivesConfig, ChangesAfterConfig, DelConfig, GetConfig, GetInfoConfig, SetConfig } from "./IArchives";
861
+ import { IArchives, ArchiveFileInfo, ArchivesConfig, ChangesAfterConfig, DelConfig, FindConfig, GetConfig, GetInfoConfig, SetConfig } from "./IArchives";
862
862
  export declare class ArchivesDisk implements IArchives {
863
863
  private folder;
864
864
  constructor(folder: string);
@@ -889,14 +889,8 @@ declare module "sliftutils/storage/ArchivesDisk" {
889
889
  writeTime: number;
890
890
  size: number;
891
891
  } | undefined>;
892
- find(prefix: string, config?: {
893
- shallow?: boolean;
894
- type: "files" | "folders";
895
- }): Promise<string[]>;
896
- findInfo(prefix: string, config?: {
897
- shallow?: boolean;
898
- type?: "files" | "folders";
899
- }): Promise<ArchiveFileInfo[]>;
892
+ find(prefix: string, config?: FindConfig): Promise<string[]>;
893
+ findInfo(prefix: string, config?: FindConfig): Promise<ArchiveFileInfo[]>;
900
894
  private collectFiles;
901
895
  setLargeFile(config: {
902
896
  path: string;
@@ -909,10 +903,7 @@ declare module "sliftutils/storage/ArchivesDisk" {
909
903
  cancelLargeUpload(id: string): Promise<void>;
910
904
  getURL(path: string): Promise<string>;
911
905
  }
912
- export declare function applyFindInfoShape(files: ArchiveFileInfo[], prefix: string, config?: {
913
- shallow?: boolean;
914
- type?: "files" | "folders";
915
- }): ArchiveFileInfo[];
906
+ export declare function applyFindInfoShape(files: ArchiveFileInfo[], prefix: string, config?: FindConfig): ArchiveFileInfo[];
916
907
 
917
908
  }
918
909
 
@@ -2109,7 +2100,9 @@ declare module "sliftutils/storage/IArchives" {
2109
2100
 
2110
2101
  NOTE: If we're in the browser, we should allow downloading the files via the URL form (if it's a public bucket), however, we won't allow writing, because their servers do not allow secure browser writes.
2111
2102
  */
2112
- export type RemoteConfigBase = string | HostedConfig | BackblazeConfig;
2103
+ export type RemoteConfigBase = string | SourceConfig;
2104
+ /** One configured source in a routing config: a hosted (our storage server) or backblaze entry. Requests carry the exact SourceConfig they selected, and the server matches it against its own entries to pick the backing store. */
2105
+ export type SourceConfig = HostedConfig | BackblazeConfig;
2113
2106
  export type CommonConfig = {
2114
2107
  /** By default a server hosting this bucket eagerly copies this source's full contents onto its own disk (on top of the lazy read-through caching). Set this to be a front end for a very large database without copying the full database - reads still down-cache individual files on demand. */
2115
2108
  noFullSync?: boolean;
@@ -2148,6 +2141,14 @@ declare module "sliftutils/storage/IArchives" {
2148
2141
  noFallbacks?: boolean;
2149
2142
  /** Store-to-store call: the serving node answers purely from its own disk, completely short-circuiting its index holders - chasing its own remote holders while answering another store is how infinite get loops between stores form (A asks B, B's index points back at A, ...). No window or route checks on reads: if the bytes are on its disk, the caller may have them. */
2150
2143
  internal?: boolean;
2144
+ /** Also return size-0 results (tombstones - an empty file IS a missing file) instead of treating them as absent. Off by default, matching getInfo's flag of the same name. Synchronization passes this so a DELETED file (with its write time) is distinguishable from a file that never existed. */
2145
+ includeTombstones?: boolean;
2146
+ };
2147
+ export type FindConfig = {
2148
+ shallow?: boolean;
2149
+ type?: "files" | "folders";
2150
+ /** Listings normally come ONLY from the authoritative sources (the same nodes writes go to - read-your-writes). With fallbacks, a failing shard's routes are covered by the next source holding them (e.g. a wide read replica) instead of the call failing - high availability at the cost of possibly missing just-written data. Single-source archives ignore the flag. */
2151
+ fallbacks?: boolean;
2151
2152
  };
2152
2153
  export type DelConfig = {
2153
2154
  /** Stamps the deletion (its tombstone) with this write time instead of now. Synchronization passes the ORIGINAL deletion time, so deletion ordering survives propagation exactly like any other write's ordering. */
@@ -2170,7 +2171,7 @@ declare module "sliftutils/storage/IArchives" {
2170
2171
  lastModified?: number;
2171
2172
  /** Makes the write acceptable on immutable targets: an existing path is simply kept (immutability wins - nothing is overwritten) instead of the write throwing. Requires lastModified. Synchronization MUST pass this on every push - a plain set throws on immutable targets, which would abort reconciliation whenever one source in a chain is immutable. */
2172
2173
  forceSetImmutable?: boolean;
2173
- /** Skips the target-side safety reads around the write (backblaze: the pre-write getInfo comparison and the post-upload existence poll). For writers whose own bookkeeping already decides what to write and orders it by write time (BlobStore's index-driven writes and synchronization), those reads are pure extra API calls - but the default stays checked, because other users of the raw backends rely on the checks. */
2174
+ /** Skips REDUNDANT target-side safety reads around the write (backblaze: the post-upload existence poll). It does NOT skip checks that are the target's only ordering guard: backblaze's pre-write comparison stays, because b2 has no server of ours enforcing only-take-the-latest - without it a stale push lands over a newer value or tombstone and b2's self-stamped upload time launders it into the newest copy in the system (global resurrection). Hosted targets re-check server-side, so their client-side shortcuts are safe. */
2174
2175
  noChecks?: boolean;
2175
2176
  /** Store-to-store push: the receiving node writes purely to its own disk and index, with NO downstream fan-out (the pushing store owns propagation - fanning its pushes back out is how write loops between stores form). Window and route ARE still checked: the stamp must fall inside one of the receiver's configured windows and routes, so a confused peer cannot stuff data onto a node that was never meant to hold it. Requires lastModified. */
2176
2177
  internal?: boolean;
@@ -2309,15 +2310,9 @@ declare module "sliftutils/storage/IArchives" {
2309
2310
  * their empty files - that is how scans learn of deletions - but nothing built on the index
2310
2311
  * ever surfaces them.
2311
2312
  */
2312
- find(prefix: string, config?: {
2313
- shallow?: boolean;
2314
- type: "files" | "folders";
2315
- }): Promise<string[]>;
2313
+ find(prefix: string, config?: FindConfig): Promise<string[]>;
2316
2314
  /** See find for the empty-file (tombstone) rule. */
2317
- findInfo(prefix: string, config?: {
2318
- shallow?: boolean;
2319
- type: "files" | "folders";
2320
- }): Promise<ArchiveFileInfo[]>;
2315
+ findInfo(prefix: string, config?: FindConfig): Promise<ArchiveFileInfo[]>;
2321
2316
  /** Only works for public buckets (private buckets are API-access only). */
2322
2317
  getURL(path: string): Promise<string>;
2323
2318
  /** The bucket's configuration, which tells whether the optional functions are supported. */
@@ -2592,7 +2587,7 @@ declare module "sliftutils/storage/TransactionStorage" {
2592
2587
  declare module "sliftutils/storage/backblaze" {
2593
2588
  /// <reference types="node" />
2594
2589
  /// <reference types="node" />
2595
- import { IArchives, ArchivesConfig, ChangesAfterConfig, ArchiveFileInfo, DelConfig, GetConfig, GetInfoConfig, SetConfig } from "./IArchives";
2590
+ import { IArchives, ArchivesConfig, ChangesAfterConfig, ArchiveFileInfo, DelConfig, FindConfig, GetConfig, GetInfoConfig, SetConfig } from "./IArchives";
2596
2591
  export declare class ArchivesBackblaze implements IArchives {
2597
2592
  private config;
2598
2593
  constructor(config: {
@@ -2631,15 +2626,9 @@ declare module "sliftutils/storage/backblaze" {
2631
2626
  writeTime: number;
2632
2627
  size: number;
2633
2628
  } | undefined>;
2634
- find(prefix: string, config?: {
2635
- shallow?: boolean;
2636
- type: "files" | "folders";
2637
- }): Promise<string[]>;
2629
+ find(prefix: string, config?: FindConfig): Promise<string[]>;
2638
2630
  getChangesAfter2(config: ChangesAfterConfig): Promise<ArchiveFileInfo[]>;
2639
- findInfo(prefix: string, config?: {
2640
- shallow?: boolean;
2641
- type: "files" | "folders";
2642
- }): Promise<{
2631
+ findInfo(prefix: string, config?: FindConfig): Promise<{
2643
2632
  path: string;
2644
2633
  createTime: number;
2645
2634
  size: number;
@@ -2896,10 +2885,12 @@ declare module "sliftutils/storage/remoteFileStorage" {
2896
2885
  declare module "sliftutils/storage/remoteStorage/ArchivesRemote" {
2897
2886
  /// <reference types="node" />
2898
2887
  /// <reference types="node" />
2899
- import { IArchives, ArchiveFileInfo, ArchivesConfig, ArchivesSyncStatus, ChangesAfterConfig, DelConfig, GetConfig, GetInfoConfig, SetConfig } from "../IArchives";
2888
+ import { IArchives, ArchiveFileInfo, ArchivesConfig, ArchivesSyncStatus, ChangesAfterConfig, DelConfig, FindConfig, GetConfig, GetInfoConfig, SourceConfig, SetConfig } from "../IArchives";
2900
2889
  export type ArchivesRemoteConfig = {
2901
2890
  url: string;
2902
2891
  waitForAccess?: boolean;
2892
+ /** The exact routing-config entry this connection represents, sent with every call so the server picks the matching per-route store (one server hosts one store per route). Instances built from a bare URL fabricate one - it will never match, which only works for calls that don't select a store (internal reads, ROUTING_FILE, getConfig). */
2893
+ sourceConfig: SourceConfig;
2903
2894
  };
2904
2895
  export declare function parseStorageUrl(url: string): {
2905
2896
  address: string;
@@ -2947,14 +2938,8 @@ declare module "sliftutils/storage/remoteStorage/ArchivesRemote" {
2947
2938
  writeTime: number;
2948
2939
  size: number;
2949
2940
  } | undefined>;
2950
- findInfo(prefix: string, config?: {
2951
- shallow?: boolean;
2952
- type: "files" | "folders";
2953
- }): Promise<ArchiveFileInfo[]>;
2954
- find(prefix: string, config?: {
2955
- shallow?: boolean;
2956
- type: "files" | "folders";
2957
- }): Promise<string[]>;
2941
+ findInfo(prefix: string, config?: FindConfig): Promise<ArchiveFileInfo[]>;
2942
+ find(prefix: string, config?: FindConfig): Promise<string[]>;
2958
2943
  getChangesAfter2(config: ChangesAfterConfig): Promise<ArchiveFileInfo[]>;
2959
2944
  getConfig(): Promise<ArchivesConfig>;
2960
2945
  getSyncStatus(): Promise<ArchivesSyncStatus>;
@@ -2971,7 +2956,7 @@ declare module "sliftutils/storage/remoteStorage/ArchivesRemote" {
2971
2956
  declare module "sliftutils/storage/remoteStorage/ArchivesUrl" {
2972
2957
  /// <reference types="node" />
2973
2958
  /// <reference types="node" />
2974
- import { IArchives, ArchiveFileInfo, ArchivesConfig, ChangesAfterConfig, GetConfig, GetInfoConfig } from "../IArchives";
2959
+ import { IArchives, ArchiveFileInfo, ArchivesConfig, ChangesAfterConfig, FindConfig, GetConfig, GetInfoConfig } from "../IArchives";
2975
2960
  export declare class ArchivesUrl implements IArchives {
2976
2961
  private base;
2977
2962
  constructor(base: string);
@@ -2995,14 +2980,8 @@ declare module "sliftutils/storage/remoteStorage/ArchivesUrl" {
2995
2980
  path: string;
2996
2981
  getNextData(): Promise<Buffer | undefined>;
2997
2982
  }): Promise<void>;
2998
- find(prefix: string, config?: {
2999
- shallow?: boolean;
3000
- type: "files" | "folders";
3001
- }): Promise<string[]>;
3002
- findInfo(prefix: string, config?: {
3003
- shallow?: boolean;
3004
- type: "files" | "folders";
3005
- }): Promise<ArchiveFileInfo[]>;
2983
+ find(prefix: string, config?: FindConfig): Promise<string[]>;
2984
+ findInfo(prefix: string, config?: FindConfig): Promise<ArchiveFileInfo[]>;
3006
2985
  getChangesAfter2(config: ChangesAfterConfig): Promise<ArchiveFileInfo[]>;
3007
2986
  getURL(path: string): Promise<string>;
3008
2987
  getConfig(): Promise<ArchivesConfig>;
@@ -3034,6 +3013,8 @@ declare module "sliftutils/storage/remoteStorage/accessStats" {
3034
3013
  path: string;
3035
3014
  size?: number;
3036
3015
  }): void;
3016
+ /** Method decorator factory, for API methods whose single config-object argument has account and bucketName: tracks the access (as `bucketName/path`) after the method succeeds. Sizes come from the config's data (writes) or the result's data (reads); operations without either are count-only. Array results (listings) track one access per returned path, or a single access at the prefix when empty. */
3017
+ export declare function trackAccessCall(operation: string): (target: unknown, key: string, descriptor: PropertyDescriptor) => void;
3037
3018
  export declare function getAccessTotals(account: string): AccessTotals;
3038
3019
  export declare function readAccessSummaries(config: {
3039
3020
  account: string;
@@ -3048,38 +3029,47 @@ declare module "sliftutils/storage/remoteStorage/accessStats" {
3048
3029
  declare module "sliftutils/storage/remoteStorage/blobStore" {
3049
3030
  /// <reference types="node" />
3050
3031
  /// <reference types="node" />
3051
- import { IArchives, ArchiveFileInfo, ArchivesSource, ArchivesSyncStatus, ChangesAfterConfig, GetConfig, GetInfoConfig, SyncActivity } from "../IArchives";
3032
+ import { IArchives, ArchiveFileInfo, ArchivesSource, ArchivesSyncStatus, ChangesAfterConfig, FindConfig, HostedConfig, SyncActivity } from "../IArchives";
3033
+ import { ArchivesDisk } from "../ArchivesDisk";
3052
3034
  export declare const DEFAULT_FAST_WRITE_DELAY: number;
3053
3035
  export declare const WINDOW_END_FLUSH_MARGIN: number;
3054
- export type WriteConfig = {
3055
- fast?: boolean;
3056
- writeDelay?: number;
3057
- lastModified?: number;
3058
- };
3059
3036
  export type IBucketStore = {
3060
- get(fileName: string, config?: GetConfig): Promise<Buffer | undefined>;
3061
- get2(fileName: string, config?: GetConfig): Promise<{
3037
+ /** internal (store-to-store) reads answer purely from the local disk; see GetConfig.internal */
3038
+ get2(config: {
3039
+ path: string;
3040
+ range?: {
3041
+ start: number;
3042
+ end: number;
3043
+ };
3044
+ internal?: boolean;
3045
+ includeTombstones?: boolean;
3046
+ }): Promise<{
3062
3047
  data: Buffer;
3063
3048
  writeTime: number;
3064
3049
  size: number;
3065
3050
  } | undefined>;
3066
- getInternal2?(fileName: string, config?: GetConfig): Promise<{
3051
+ /** internal (store-to-store) writes go to the local disk + index with no fan-out; see SetConfig.internal */
3052
+ set(config: {
3053
+ path: string;
3067
3054
  data: Buffer;
3068
- writeTime: number;
3069
- size: number;
3070
- } | undefined>;
3071
- setInternal?(fileName: string, data: Buffer, config: {
3072
- lastModified: number;
3055
+ lastModified?: number;
3056
+ forceSetImmutable?: boolean;
3057
+ internal?: boolean;
3073
3058
  }): Promise<void>;
3074
- set(fileName: string, data: Buffer, config?: WriteConfig): Promise<string>;
3075
- del(fileName: string, config?: WriteConfig): Promise<void>;
3076
- getInfo(fileName: string, config?: GetInfoConfig): Promise<{
3059
+ del(config: {
3060
+ path: string;
3061
+ lastModified?: number;
3062
+ internal?: boolean;
3063
+ }): Promise<void>;
3064
+ getInfo(config: {
3065
+ path: string;
3066
+ includeTombstones?: boolean;
3067
+ }): Promise<{
3077
3068
  writeTime: number;
3078
3069
  size: number;
3079
3070
  } | undefined>;
3080
- findInfo(prefix: string, config?: {
3081
- shallow?: boolean;
3082
- type?: "files" | "folders";
3071
+ findInfo(config: FindConfig & {
3072
+ prefix: string;
3083
3073
  }): Promise<ArchiveFileInfo[]>;
3084
3074
  getChangesAfter2(config: ChangesAfterConfig): Promise<ArchiveFileInfo[]>;
3085
3075
  getSyncStatus?(): Promise<ArchivesSyncStatus>;
@@ -3105,11 +3095,78 @@ declare module "sliftutils/storage/remoteStorage/blobStore" {
3105
3095
  byteCount: number;
3106
3096
  }[];
3107
3097
  }>;
3108
- startLargeUpload(): Promise<string>;
3109
- appendLargeUpload(id: string, data: Buffer): Promise<void>;
3110
- finishLargeUpload(id: string, key: string, lastModified?: number): Promise<void>;
3111
- cancelLargeUpload(id: string): Promise<void>;
3098
+ /** path/lastModified let the store reject an upload into an immutable bucket before any bytes move */
3099
+ startLargeUpload(config?: {
3100
+ path?: string;
3101
+ lastModified?: number;
3102
+ }): Promise<string>;
3103
+ appendLargeUpload(config: {
3104
+ id: string;
3105
+ data: Buffer;
3106
+ }): Promise<void>;
3107
+ finishLargeUpload(config: {
3108
+ id: string;
3109
+ path: string;
3110
+ lastModified?: number;
3111
+ }): Promise<void>;
3112
+ cancelLargeUpload(config: {
3113
+ id: string;
3114
+ }): Promise<void>;
3112
3115
  };
3116
+ /** rawDisk buckets: the disk IS the store. No index, no synchronization, no window/route/immutability validation. */
3117
+ export declare class RawDiskStore implements IBucketStore {
3118
+ private disk;
3119
+ constructor(disk: ArchivesDisk);
3120
+ get2(config: {
3121
+ path: string;
3122
+ range?: {
3123
+ start: number;
3124
+ end: number;
3125
+ };
3126
+ internal?: boolean;
3127
+ includeTombstones?: boolean;
3128
+ }): Promise<{
3129
+ data: Buffer;
3130
+ writeTime: number;
3131
+ size: number;
3132
+ } | undefined>;
3133
+ set(config: {
3134
+ path: string;
3135
+ data: Buffer;
3136
+ lastModified?: number;
3137
+ forceSetImmutable?: boolean;
3138
+ internal?: boolean;
3139
+ }): Promise<void>;
3140
+ del(config: {
3141
+ path: string;
3142
+ lastModified?: number;
3143
+ internal?: boolean;
3144
+ }): Promise<void>;
3145
+ getInfo(config: {
3146
+ path: string;
3147
+ includeTombstones?: boolean;
3148
+ }): Promise<{
3149
+ writeTime: number;
3150
+ size: number;
3151
+ } | undefined>;
3152
+ findInfo(config: FindConfig & {
3153
+ prefix: string;
3154
+ }): Promise<ArchiveFileInfo[]>;
3155
+ getChangesAfter2(config: ChangesAfterConfig): Promise<ArchiveFileInfo[]>;
3156
+ startLargeUpload(): Promise<string>;
3157
+ appendLargeUpload(config: {
3158
+ id: string;
3159
+ data: Buffer;
3160
+ }): Promise<void>;
3161
+ finishLargeUpload(config: {
3162
+ id: string;
3163
+ path: string;
3164
+ lastModified?: number;
3165
+ }): Promise<void>;
3166
+ cancelLargeUpload(config: {
3167
+ id: string;
3168
+ }): Promise<void>;
3169
+ }
3113
3170
  export type BlobSourceSpec = {
3114
3171
  identity: string;
3115
3172
  url: string;
@@ -3128,47 +3185,51 @@ declare module "sliftutils/storage/remoteStorage/blobStore" {
3128
3185
  readerDiskLimit?: number | undefined;
3129
3186
  onWriteCounted?: ((kind: "original" | "flushed", bytes: number) => void) | undefined;
3130
3187
  resolveSourceUrl?: ((url: string) => IArchives) | undefined;
3188
+ entries?: HostedConfig[] | undefined;
3131
3189
  } | undefined);
3132
- private stopped;
3133
- private index;
3134
- private mem;
3135
- private indexFileCount;
3136
- private indexByteCount;
3137
- private sourceFileCounts;
3138
- private sourceByteCounts;
3139
- private syncActivities;
3140
- private dirty;
3141
- private overlay;
3142
- private sourceStates;
3143
- private syncStarted;
3144
- private sourcesList;
3145
- private slotSourcesListIndexes;
3146
- private slotRegistrations;
3147
- private isLive;
3148
- private registerSlot;
3149
- private sourcesListIndexOfSlot;
3150
- private slotForSourcesListIndex;
3151
- private getEntryHolder;
3152
3190
  init: {
3153
3191
  (): Promise<void>;
3154
3192
  reset(): void;
3155
3193
  set(newValue: Promise<void>): void;
3156
3194
  };
3157
3195
  dispose(): Promise<void>;
3158
- private loadIndex;
3159
- private countEntry;
3160
- private setIndexEntry;
3161
- private deleteIndexEntry;
3162
- /** Applies a config change to the RUNNING store: windows/routes update in place, new sources are added (their sync starts immediately), and removed sources' slots go dead (their scans stop, their index entries drop). The store survives every routine config evolution - it is never destroyed for a source-list change, only for structural flips it cannot express (rawDisk). Pending fast writes are re-capped to the new flush deadline (flushing immediately when it has already passed). */
3163
- updateSources(specs: BlobSourceSpec[]): void;
3164
- private removeSource;
3165
- /** Rescans our own disk's metadata into the index - used around valid window handoffs, where another process wrote files to the shared folder that our index hasn't seen. */
3166
- rescanBase(): Promise<void>;
3167
- /** A boundary scan of the node that owned (part of) our route in the valid window before ours, when that node is different storage (a disk rescan can't see its writes): just its changes since the boundary neighborhood, with matching values pulled onto our own disk. */
3168
- boundaryScanRemote(source: IArchives, config: {
3169
- since: number;
3170
- route?: [number, number];
3196
+ get2(config: {
3197
+ path: string;
3198
+ range?: {
3199
+ start: number;
3200
+ end: number;
3201
+ };
3202
+ internal?: boolean;
3203
+ includeTombstones?: boolean;
3204
+ }): Promise<{
3205
+ data: Buffer;
3206
+ writeTime: number;
3207
+ size: number;
3208
+ } | undefined>;
3209
+ set(config: {
3210
+ path: string;
3211
+ data: Buffer;
3212
+ lastModified?: number;
3213
+ forceSetImmutable?: boolean;
3214
+ internal?: boolean;
3171
3215
  }): Promise<void>;
3216
+ del(config: {
3217
+ path: string;
3218
+ lastModified?: number;
3219
+ internal?: boolean;
3220
+ }): Promise<void>;
3221
+ getInfo(config: {
3222
+ path: string;
3223
+ includeTombstones?: boolean;
3224
+ }): Promise<{
3225
+ writeTime: number;
3226
+ size: number;
3227
+ } | undefined>;
3228
+ findInfo(config: FindConfig & {
3229
+ prefix: string;
3230
+ }): Promise<ArchiveFileInfo[]>;
3231
+ getChangesAfter2(config: ChangesAfterConfig): Promise<ArchiveFileInfo[]>;
3232
+ getSyncStatus(): Promise<ArchivesSyncStatus>;
3172
3233
  /** The cheap always-current totals plus any in-progress background synchronization. */
3173
3234
  getSyncProgress(): {
3174
3235
  index: {
@@ -3193,7 +3254,60 @@ declare module "sliftutils/storage/remoteStorage/blobStore" {
3193
3254
  byteCount: number;
3194
3255
  }[];
3195
3256
  }>;
3257
+ /** Applies a config change to the RUNNING store: windows/routes update in place, new sources are added (their sync starts immediately), and removed sources' slots go dead (their scans stop, their index entries drop). The store survives every routine config evolution - it is never destroyed for a source-list change, only for structural flips it cannot express (rawDisk). Pending fast writes are re-capped to the new flush deadline (flushing immediately when it has already passed). */
3258
+ updateSources(specs: BlobSourceSpec[], entries?: HostedConfig[]): void;
3259
+ /** Rescans our own disk's metadata into the index - used around valid window handoffs, where another process wrote files to the shared folder that our index hasn't seen. */
3260
+ rescanBase(): Promise<void>;
3261
+ /** A boundary scan of the node that owned (part of) our route in the valid window before ours, when that node is different storage (a disk rescan can't see its writes): just its changes since the boundary neighborhood, with matching values pulled onto our own disk. */
3262
+ boundaryScanRemote(source: IArchives, config: {
3263
+ since: number;
3264
+ route?: [number, number];
3265
+ }): Promise<void>;
3266
+ startLargeUpload(config?: {
3267
+ path?: string;
3268
+ lastModified?: number;
3269
+ }): Promise<string>;
3270
+ appendLargeUpload(config: {
3271
+ id: string;
3272
+ data: Buffer;
3273
+ }): Promise<void>;
3274
+ finishLargeUpload(config: {
3275
+ id: string;
3276
+ path: string;
3277
+ lastModified?: number;
3278
+ }): Promise<void>;
3279
+ cancelLargeUpload(config: {
3280
+ id: string;
3281
+ }): Promise<void>;
3282
+ private stopped;
3283
+ private index;
3284
+ private mem;
3285
+ private indexFileCount;
3286
+ private indexByteCount;
3287
+ private sourceFileCounts;
3288
+ private sourceByteCounts;
3289
+ private syncActivities;
3290
+ private dirty;
3291
+ private overlay;
3292
+ private sourceStates;
3293
+ private syncStarted;
3294
+ private entries;
3295
+ private sourcesList;
3296
+ private slotSourcesListIndexes;
3297
+ private slotRegistrations;
3298
+ private isLive;
3299
+ private registerSlot;
3300
+ private sourcesListIndexOfSlot;
3301
+ private slotForSourcesListIndex;
3302
+ private getEntryHolder;
3303
+ private loadIndex;
3304
+ private countEntry;
3305
+ private setIndexEntry;
3306
+ private deleteIndexEntry;
3307
+ private removeSource;
3196
3308
  private flushIndex;
3309
+ private assertMutable;
3310
+ private assertInternalWriteAccepted;
3197
3311
  private runSourceSync;
3198
3312
  private isDeadIntermediate;
3199
3313
  private scanSource;
@@ -3204,43 +3318,15 @@ declare module "sliftutils/storage/remoteStorage/blobStore" {
3204
3318
  private waitForRequiredScans;
3205
3319
  private checkMissingKey;
3206
3320
  private getIndexEntry;
3207
- get(key: string, config?: GetConfig): Promise<Buffer | undefined>;
3208
- get2(key: string, config?: GetConfig): Promise<{
3209
- data: Buffer;
3210
- writeTime: number;
3211
- size: number;
3212
- } | undefined>;
3213
3321
  /** Internal (store-to-store) read: purely the local disk, completely short-circuiting the index and holder resolution - the caller is another store, and chasing OUR remote holders while answering it is how infinite get loops between stores form. No window or route checks: if the bytes are on our disk, the caller may have them. Note fast writes still sitting in the overlay are invisible here; the caller re-finds them after our flush. */
3214
- getInternal2(key: string, config?: GetConfig): Promise<{
3215
- data: Buffer;
3216
- writeTime: number;
3217
- size: number;
3218
- } | undefined>;
3219
- /** Internal (store-to-store) write: the local disk plus our index, with NO downstream fan-out - the pushing store owns propagation, and fanning its pushes back out is how write loops between stores form. Window/route acceptance is the caller's (writeBucketFile's) job; only-take-latest still applies here. */
3220
- setInternal(key: string, data: Buffer, config: {
3221
- lastModified: number;
3222
- }): Promise<void>;
3322
+ private getInternal2;
3323
+ /** Internal (store-to-store) write: the local disk plus our index, with NO downstream fan-out - the pushing store owns propagation, and fanning its pushes back out is how write loops between stores form. Only-take-latest still applies here. */
3324
+ private setInternal;
3223
3325
  private cacheRead;
3224
- set(key: string, data: Buffer, config?: WriteConfig): Promise<string>;
3225
- del(key: string, config?: WriteConfig): Promise<void>;
3226
3326
  private setOrDelete;
3227
3327
  private getWritableSources;
3228
3328
  private writeToSources;
3229
- getInfo(key: string, config?: GetInfoConfig): Promise<{
3230
- writeTime: number;
3231
- size: number;
3232
- } | undefined>;
3233
- findInfo(prefix: string, config?: {
3234
- shallow?: boolean;
3235
- type?: "files" | "folders";
3236
- }): Promise<ArchiveFileInfo[]>;
3237
- getChangesAfter2(config: ChangesAfterConfig): Promise<ArchiveFileInfo[]>;
3238
- getSyncStatus(): Promise<ArchivesSyncStatus>;
3239
3329
  private getDiskSource;
3240
- startLargeUpload(): Promise<string>;
3241
- appendLargeUpload(id: string, data: Buffer): Promise<void>;
3242
- finishLargeUpload(id: string, key: string, lastModified?: number): Promise<void>;
3243
- cancelLargeUpload(id: string): Promise<void>;
3244
3330
  private flushOverlay;
3245
3331
  private evicting;
3246
3332
  private enforceDiskLimit;
@@ -3249,6 +3335,28 @@ declare module "sliftutils/storage/remoteStorage/blobStore" {
3249
3335
 
3250
3336
  }
3251
3337
 
3338
+ declare module "sliftutils/storage/remoteStorage/bucketDisk" {
3339
+ /// <reference types="node" />
3340
+ /// <reference types="node" />
3341
+ import { RemoteConfig } from "../IArchives";
3342
+ export declare function getBucketFolder(account: string, bucketName: string, route?: [number, number]): string;
3343
+ export declare function readRoutingFile(folder: string): Promise<Buffer | undefined>;
3344
+ export declare function readRoutingFromDisk(account: string, bucketName: string): Promise<RemoteConfig | undefined>;
3345
+ /** The routing file lives ONLY in the plain (routeless) bucket folder - it is what DEFINES the per-route stores, so it cannot live inside any of them. Served directly for reads (the stores never hold it). */
3346
+ export declare function getRoutingFileResult(account: string, bucketName: string): Promise<{
3347
+ data: Buffer;
3348
+ writeTime: number;
3349
+ size: number;
3350
+ } | undefined>;
3351
+ export type BucketDiskInfo = {
3352
+ totalBytes: number;
3353
+ freeBytes: number;
3354
+ usedBytes: number;
3355
+ };
3356
+ export declare function getDiskInfo(folder: string): Promise<BucketDiskInfo>;
3357
+
3358
+ }
3359
+
3252
3360
  declare module "sliftutils/storage/remoteStorage/cliArgs" {
3253
3361
  export declare function getArg(name: string): string | undefined;
3254
3362
 
@@ -3257,11 +3365,11 @@ declare module "sliftutils/storage/remoteStorage/cliArgs" {
3257
3365
  declare module "sliftutils/storage/remoteStorage/createArchives" {
3258
3366
  /// <reference types="node" />
3259
3367
  /// <reference types="node" />
3260
- import { IArchives, RemoteConfig, RemoteConfigBase, HostedConfig, BackblazeConfig, ArchiveFileInfo, ArchivesConfig, ArchivesSyncStatus, ChangesAfterConfig, DelConfig, GetConfig, GetInfoConfig, SetConfig } from "../IArchives";
3368
+ import { IArchives, RemoteConfig, RemoteConfigBase, SourceConfig, ArchiveFileInfo, ArchivesConfig, ArchivesSyncStatus, ChangesAfterConfig, DelConfig, FindConfig, GetConfig, GetInfoConfig, SetConfig } from "../IArchives";
3261
3369
  import { ServerBucketInfo, ActiveBucketInfo } from "./storageServerState";
3262
3370
  /** The address, port, account, and bucket name a bucket routing URL addresses. Throws when the URL isn't a hosted bucket routing URL (https://host:port/file/<account>/<bucketName>/storage/storagerouting.json). */
3263
3371
  export { parseHostedUrl, parseBackblazeUrl, getBucketBaseUrl } from "./remoteConfig";
3264
- export declare function createApiArchives(source: HostedConfig | BackblazeConfig): IArchives;
3372
+ export declare function createApiArchives(source: SourceConfig): IArchives;
3265
3373
  export type ArchivesChainOptions = {
3266
3374
  /** Outside of node we default to read-only downloads over the public URLs (no API connection) when the config has public sources. Set this to connect to the API anyway - needed for writing, listing, and any other operation the plain URL form cannot serve. */
3267
3375
  directConnect?: boolean;
@@ -3339,14 +3447,8 @@ declare module "sliftutils/storage/remoteStorage/createArchives" {
3339
3447
  } | undefined>;
3340
3448
  private selectCoveringSources;
3341
3449
  private runOnCovering;
3342
- find(prefix: string, config?: {
3343
- shallow?: boolean;
3344
- type: "files" | "folders";
3345
- }): Promise<string[]>;
3346
- findInfo(prefix: string, config?: {
3347
- shallow?: boolean;
3348
- type: "files" | "folders";
3349
- }): Promise<ArchiveFileInfo[]>;
3450
+ find(prefix: string, config?: FindConfig): Promise<string[]>;
3451
+ findInfo(prefix: string, config?: FindConfig): Promise<ArchiveFileInfo[]>;
3350
3452
  getChangesAfter2(config: ChangesAfterConfig): Promise<ArchiveFileInfo[]>;
3351
3453
  getSyncStatus(): Promise<ArchivesSyncStatus>;
3352
3454
  getConfig(): Promise<ArchivesConfig>;
@@ -3440,12 +3542,11 @@ declare module "sliftutils/storage/remoteStorage/grantAccessCli" {
3440
3542
  }
3441
3543
 
3442
3544
  declare module "sliftutils/storage/remoteStorage/intermediateSources" {
3443
- import { RemoteConfig, HostedConfig, BackblazeConfig } from "../IArchives";
3545
+ import { RemoteConfig, SourceConfig } from "../IArchives";
3444
3546
  export declare const INTERMEDIATE_EXPIRE_GRACE: number;
3445
3547
  /** Adding or removing intermediates is a real config update, so it takes a real version increment - but a proportional one, so it stays far below whatever the author's next version would be (whether they count 1, 2, 3 or use timestamps), and a million of them still fit under it. */
3446
3548
  export declare function nextIntermediateVersion(version: number): number;
3447
- type ObjectSource = HostedConfig | BackblazeConfig;
3448
- export declare function getIntermediateSources(config: RemoteConfig): ObjectSource[];
3549
+ export declare function getIntermediateSources(config: RemoteConfig): SourceConfig[];
3449
3550
  export declare function hasIntermediateSources(config: RemoteConfig): boolean;
3450
3551
  /** Removes every intermediate entry and rejoins the windows it split, giving back the underlying configuration. Two configs that resolve equal differ only by intermediates. */
3451
3552
  export declare function resolveIntermediateSources(config: RemoteConfig): RemoteConfig;
@@ -3459,8 +3560,7 @@ declare module "sliftutils/storage/remoteStorage/intermediateSources" {
3459
3560
  /** Intermediates whose window ended more than INTERMEDIATE_EXPIRE_GRACE ago are removed, and the windows they split are rejoined. */
3460
3561
  export declare function expireIntermediateSources(config: RemoteConfig, now: number): RemoteConfig;
3461
3562
  /** The url of the entry an intermediate was split out of - the neighbour it touches. */
3462
- export declare function findSplitUrl(config: RemoteConfig, intermediate: ObjectSource): string | undefined;
3463
- export {};
3563
+ export declare function findSplitUrl(config: RemoteConfig, intermediate: SourceConfig): string | undefined;
3464
3564
 
3465
3565
  }
3466
3566
 
@@ -3471,7 +3571,7 @@ declare module "sliftutils/storage/remoteStorage/productionEnv" {
3471
3571
  declare module "sliftutils/storage/remoteStorage/remoteConfig" {
3472
3572
  /// <reference types="node" />
3473
3573
  /// <reference types="node" />
3474
- import { RemoteConfig, RemoteConfigBase, HostedConfig, BackblazeConfig, ArchiveFileInfo, ChangesAfterConfig } from "../IArchives";
3574
+ import { RemoteConfig, RemoteConfigBase, SourceConfig, ArchiveFileInfo, ChangesAfterConfig } from "../IArchives";
3475
3575
  export declare const ROUTING_FILE = "storage/storagerouting.json";
3476
3576
  /** The variable-shard route override embedded in the key ("<sentinel>_<value>", see VARIABLE_SHARD), or undefined when the key has no sentinel or the sentinel has no value yet. */
3477
3577
  export declare function parseVariableRoute(key: string): number | undefined;
@@ -3497,21 +3597,52 @@ declare module "sliftutils/storage/remoteStorage/remoteConfig" {
3497
3597
  bucketName: string;
3498
3598
  };
3499
3599
  export declare function replaceHostedUrlPort(url: string, port: number): string;
3500
- export declare function normalizeSource(source: RemoteConfigBase): HostedConfig | BackblazeConfig;
3600
+ export declare function normalizeSource(source: RemoteConfigBase): SourceConfig;
3501
3601
  export declare function normalizeRemoteConfig(config: RemoteConfig | RemoteConfigBase): RemoteConfig;
3502
3602
  export declare function parseRoutingData(data: Buffer): RemoteConfig;
3503
3603
  export declare function serializeRemoteConfig(config: RemoteConfig): Buffer;
3504
3604
 
3505
3605
  }
3506
3606
 
3607
+ declare module "sliftutils/storage/remoteStorage/serverConfig" {
3608
+ import type { IStorage } from "../IStorage";
3609
+ import type { AccessRequest, TrustRecord } from "./storageController";
3610
+ export type StorageServerConfig = {
3611
+ domain: string;
3612
+ port: number;
3613
+ rootDomain: string;
3614
+ sshTarget: string;
3615
+ serverCommand: string;
3616
+ folder: string;
3617
+ };
3618
+ export declare function setStorageServerConfig(value: StorageServerConfig): void;
3619
+ export declare function getStorageServerConfig(): StorageServerConfig;
3620
+ export declare function getStorageServerConfigOptional(): StorageServerConfig | undefined;
3621
+ export declare function setWritesRejectedReason(reason: string | undefined): void;
3622
+ export declare function getWritesRejectedReason(): string | undefined;
3623
+ export declare function assertWritesAllowed(): void;
3624
+ export declare function getStorageFolder(): string;
3625
+ export declare function getTrust(): Promise<IStorage<TrustRecord>>;
3626
+ export declare function getRequests(): Promise<IStorage<AccessRequest[]>>;
3627
+ export declare function setTrustedMachines(config: {
3628
+ account: string;
3629
+ machineIds: string[];
3630
+ }): Promise<void>;
3631
+ export declare function addExtraListenPort(port: number): void;
3632
+ export declare function removeExtraListenPort(port: number): void;
3633
+ /** Whether address:port is this server process. The ONE self test - findSelfIndexes, createApiArchives, and SourceWrapper all consult it, so "is this me" cannot disagree between the routing plan and connection building: a URL that is us on an extra listen port must never become a network client to ourselves, which is how infinite self-request loops form. */
3634
+ export declare function isOwnAddress(address: string, port: number): boolean;
3635
+
3636
+ }
3637
+
3507
3638
  declare module "sliftutils/storage/remoteStorage/sourceWrapper" {
3508
- import { IArchives, HostedConfig, BackblazeConfig, RemoteConfig } from "../IArchives";
3639
+ import { IArchives, SourceConfig, RemoteConfig } from "../IArchives";
3509
3640
  import { ArchivesUrl } from "./ArchivesUrl";
3510
3641
  export declare const RETRY_START_DELAY: number;
3511
3642
  export declare const RETRY_MAX_DELAY: number;
3512
3643
  export declare const RETRY_GROWTH = 1.5;
3513
3644
  export declare class SourceWrapper {
3514
- config: HostedConfig | BackblazeConfig;
3645
+ config: SourceConfig;
3515
3646
  private background;
3516
3647
  api?: IArchives;
3517
3648
  url?: ArchivesUrl;
@@ -3523,7 +3654,7 @@ declare module "sliftutils/storage/remoteStorage/sourceWrapper" {
3523
3654
  private constructor();
3524
3655
  /** Config updates routinely just move a source's valid window (the last window extends forever, then gets reduced when a new entry is appended). The wrapper survives that: only the window changes, keeping the connection, pings, and latency history. */
3525
3656
  updateValidWindow(validWindow: [number, number]): void;
3526
- static create(config: HostedConfig | BackblazeConfig, options?: {
3657
+ static create(config: SourceConfig, options?: {
3527
3658
  background?: boolean;
3528
3659
  readOnly?: boolean;
3529
3660
  }): Promise<SourceWrapper>;
@@ -3588,8 +3719,8 @@ declare module "sliftutils/storage/remoteStorage/storageClientController" {
3588
3719
  declare module "sliftutils/storage/remoteStorage/storageController" {
3589
3720
  /// <reference types="node" />
3590
3721
  /// <reference types="node" />
3591
- import { ArchiveFileInfo, ArchivesConfig, ArchivesSyncStatus, ChangesAfterConfig } from "../IArchives";
3592
- import { ServerBucketInfo, ActiveBucketInfo } from "./storageServerState";
3722
+ import { ArchiveFileInfo, ArchivesConfig, ArchivesSyncStatus, FindConfig, SourceConfig } from "../IArchives";
3723
+ import { ActiveBucketInfo, ServerBucketInfo } from "./storageServerState";
3593
3724
  import { AccessTotals, AccessSummaryState } from "./accessStats";
3594
3725
  import type { SummaryEntry } from "../../treeSummary";
3595
3726
  export declare const REMOTE_STORAGE_CLASS_GUID = "RemoteStorageController-b7e42a91";
@@ -3633,58 +3764,124 @@ declare module "sliftutils/storage/remoteStorage/storageController" {
3633
3764
  machineId: string;
3634
3765
  ip: string;
3635
3766
  }>;
3636
- requestAccess: (account: string) => Promise<{
3767
+ requestAccess: (config: {
3768
+ account: string;
3769
+ }) => Promise<{
3637
3770
  machineId: string;
3638
3771
  ip: string;
3639
3772
  requestId: string;
3640
3773
  grantAccessCommand: string;
3641
3774
  }>;
3642
- getAccessState: (account: string) => Promise<AccessState>;
3643
- listRequestsForIP: (account: string, ip: string) => Promise<AccessRequest[]>;
3644
- grantAccess: (requestId: string) => Promise<TrustRecord>;
3775
+ getAccessState: (config: {
3776
+ account: string;
3777
+ }) => Promise<AccessState>;
3778
+ listRequestsForIP: (config: {
3779
+ account: string;
3780
+ ip: string;
3781
+ }) => Promise<AccessRequest[]>;
3782
+ grantAccess: (config: {
3783
+ requestId: string;
3784
+ }) => Promise<TrustRecord>;
3645
3785
  adminListActiveBuckets: () => Promise<{
3646
3786
  account: string;
3647
3787
  bucketName: string;
3648
3788
  }[]>;
3649
- adminListRequests: (ip: string) => Promise<AccessRequest[]>;
3650
- adminGrantAccess: (requestId: string) => Promise<TrustRecord>;
3651
- get: (account: string, bucketName: string, path: string, range?: {
3652
- start: number;
3653
- end: number;
3654
- }) => Promise<Buffer | undefined>;
3655
- get2: (account: string, bucketName: string, path: string, range?: {
3656
- start: number;
3657
- end: number;
3658
- }, internal?: boolean) => Promise<{
3789
+ adminListRequests: (config: {
3790
+ ip: string;
3791
+ }) => Promise<AccessRequest[]>;
3792
+ adminGrantAccess: (config: {
3793
+ requestId: string;
3794
+ }) => Promise<TrustRecord>;
3795
+ get2: (config: {
3796
+ account: string;
3797
+ bucketName: string;
3798
+ path: string;
3799
+ sourceConfig: SourceConfig;
3800
+ range?: {
3801
+ start: number;
3802
+ end: number;
3803
+ };
3804
+ internal?: boolean;
3805
+ includeTombstones?: boolean;
3806
+ }) => Promise<{
3659
3807
  data: Buffer;
3660
3808
  writeTime: number;
3661
3809
  size: number;
3662
3810
  } | undefined>;
3663
- set: (account: string, bucketName: string, path: string, data: Buffer, lastModified?: number, forceSetImmutable?: boolean, internal?: boolean) => Promise<void>;
3664
- del: (account: string, bucketName: string, path: string, lastModified?: number, internal?: boolean) => Promise<void>;
3665
- getInfo: (account: string, bucketName: string, path: string, includeTombstones?: boolean) => Promise<{
3811
+ set: (config: {
3812
+ account: string;
3813
+ bucketName: string;
3814
+ path: string;
3815
+ data: Buffer;
3816
+ sourceConfig: SourceConfig;
3817
+ lastModified?: number;
3818
+ forceSetImmutable?: boolean;
3819
+ internal?: boolean;
3820
+ }) => Promise<void>;
3821
+ del: (config: {
3822
+ account: string;
3823
+ bucketName: string;
3824
+ path: string;
3825
+ sourceConfig: SourceConfig;
3826
+ lastModified?: number;
3827
+ internal?: boolean;
3828
+ }) => Promise<void>;
3829
+ getInfo: (config: {
3830
+ account: string;
3831
+ bucketName: string;
3832
+ path: string;
3833
+ sourceConfig: SourceConfig;
3834
+ includeTombstones?: boolean;
3835
+ }) => Promise<{
3666
3836
  writeTime: number;
3667
3837
  size: number;
3668
3838
  } | undefined>;
3669
- findInfo: (account: string, bucketName: string, prefix: string, config?: {
3670
- shallow?: boolean;
3671
- type?: "files" | "folders";
3839
+ findInfo: (config: FindConfig & {
3840
+ account: string;
3841
+ bucketName: string;
3842
+ prefix: string;
3843
+ sourceConfig: SourceConfig;
3672
3844
  }) => Promise<ArchiveFileInfo[]>;
3673
- getChangesAfter2: (account: string, bucketName: string, config: ChangesAfterConfig) => Promise<ArchiveFileInfo[]>;
3674
- getArchivesConfig: (account: string, bucketName: string) => Promise<ArchivesConfig>;
3675
- listBuckets: (account: string) => Promise<ServerBucketInfo[]>;
3676
- getActiveBucket: (account: string, bucketName: string) => Promise<ActiveBucketInfo | string>;
3677
- activateBucket: (account: string, bucketName: string) => Promise<ActiveBucketInfo | string>;
3678
- clearWriteStats: (account: string) => Promise<{
3845
+ getChangesAfter2: (config: {
3846
+ account: string;
3847
+ bucketName: string;
3848
+ sourceConfig: SourceConfig;
3849
+ time: number;
3850
+ routes?: [number, number][];
3851
+ }) => Promise<ArchiveFileInfo[]>;
3852
+ getArchivesConfig: (config: {
3853
+ account: string;
3854
+ bucketName: string;
3855
+ }) => Promise<ArchivesConfig>;
3856
+ listBuckets: (config: {
3857
+ account: string;
3858
+ }) => Promise<ServerBucketInfo[]>;
3859
+ getActiveBucket: (config: {
3860
+ account: string;
3861
+ bucketName: string;
3862
+ }) => Promise<ActiveBucketInfo | string>;
3863
+ activateBucket: (config: {
3864
+ account: string;
3865
+ bucketName: string;
3866
+ }) => Promise<ActiveBucketInfo | string>;
3867
+ clearWriteStats: (config: {
3868
+ account: string;
3869
+ }) => Promise<{
3679
3870
  clearedBuckets: number;
3680
3871
  }>;
3681
- getAccessStats: (account: string) => Promise<AccessTotals>;
3682
- getAccessSummaries: (account: string, config: {
3872
+ getAccessStats: (config: {
3873
+ account: string;
3874
+ }) => Promise<AccessTotals>;
3875
+ getAccessSummaries: (config: {
3876
+ account: string;
3683
3877
  operation: string;
3684
3878
  maxCount: number;
3685
3879
  weightBySize?: boolean;
3686
3880
  }) => Promise<SummaryEntry<AccessSummaryState>[]>;
3687
- getIndexInfo: (account: string, bucketName: string) => Promise<{
3881
+ getIndexInfo: (config: {
3882
+ account: string;
3883
+ bucketName: string;
3884
+ }) => Promise<{
3688
3885
  fileCount: number;
3689
3886
  byteCount: number;
3690
3887
  sources: {
@@ -3693,11 +3890,27 @@ declare module "sliftutils/storage/remoteStorage/storageController" {
3693
3890
  byteCount: number;
3694
3891
  }[];
3695
3892
  } | undefined>;
3696
- getSyncStatus: (account: string, bucketName: string) => Promise<ArchivesSyncStatus>;
3697
- startLargeFile: (account: string, bucketName: string, path: string, lastModified?: number) => Promise<string>;
3698
- uploadPart: (uploadId: string, data: Buffer) => Promise<void>;
3699
- finishLargeFile: (uploadId: string) => Promise<void>;
3700
- cancelLargeFile: (uploadId: string) => Promise<void>;
3893
+ getSyncStatus: (config: {
3894
+ account: string;
3895
+ bucketName: string;
3896
+ }) => Promise<ArchivesSyncStatus>;
3897
+ startLargeFile: (config: {
3898
+ account: string;
3899
+ bucketName: string;
3900
+ path: string;
3901
+ sourceConfig: SourceConfig;
3902
+ lastModified?: number;
3903
+ }) => Promise<string>;
3904
+ uploadPart: (config: {
3905
+ uploadId: string;
3906
+ data: Buffer;
3907
+ }) => Promise<void>;
3908
+ finishLargeFile: (config: {
3909
+ uploadId: string;
3910
+ }) => Promise<void>;
3911
+ cancelLargeFile: (config: {
3912
+ uploadId: string;
3913
+ }) => Promise<void>;
3701
3914
  httpEntry: (config?: {
3702
3915
  requireCalls?: string[];
3703
3916
  cacheTime?: number;
@@ -3726,73 +3939,61 @@ declare module "sliftutils/storage/remoteStorage/storageServerState" {
3726
3939
  /// <reference types="node" />
3727
3940
  /// <reference types="node" />
3728
3941
  import { IBucketStore } from "./blobStore";
3729
- import { RemoteConfig, HostedConfig, IArchives, ArchivesConfig } from "../IArchives";
3730
- import type { IStorage } from "../IStorage";
3731
- import type { AccessRequest, TrustRecord } from "./storageController";
3732
- export type StorageServerConfig = {
3733
- domain: string;
3734
- port: number;
3735
- rootDomain: string;
3736
- sshTarget: string;
3737
- serverCommand: string;
3942
+ import { RemoteConfig, HostedConfig, SourceConfig, IArchives, ArchivesConfig, ArchivesSyncStatus } from "../IArchives";
3943
+ import { BucketDiskInfo } from "./bucketDisk";
3944
+ import { SelfSummary } from "./storePlan";
3945
+ export type LoadedStore = {
3946
+ routeKey: string;
3947
+ route?: [number, number];
3948
+ entries: HostedConfig[];
3738
3949
  folder: string;
3950
+ store: IBucketStore;
3739
3951
  };
3740
- export declare function setStorageServerConfig(value: StorageServerConfig): void;
3741
- export declare function getStorageServerConfig(): StorageServerConfig;
3742
- export declare function getStorageServerConfigOptional(): StorageServerConfig | undefined;
3743
- export declare function setWritesRejectedReason(reason: string | undefined): void;
3744
- export declare function getWritesRejectedReason(): string | undefined;
3745
- export declare function assertWritesAllowed(): void;
3746
- export declare function getTrust(): Promise<IStorage<TrustRecord>>;
3747
- export declare function getRequests(): Promise<IStorage<AccessRequest[]>>;
3748
- export type BucketWriteStats = {
3749
- /** Every set call the bucket accepted */
3750
- originalWrites: number;
3751
- originalBytes: number;
3752
- /** What actually reached the sources. Fast writes coalesce repeated writes to the same key, so this is lower than the original counts (and is what the disk actually did). */
3753
- flushedWrites: number;
3754
- flushedBytes: number;
3755
- };
3756
- /** Zeroes the write statistics of every bucket in the account. */
3757
- export declare function clearAccountWriteStats(account: string): number;
3758
- export declare function setTrustedMachines(config: {
3759
- account: string;
3760
- machineIds: string[];
3761
- }): Promise<void>;
3762
- export type LoadedBucket = {
3952
+ export type BucketState = {
3763
3953
  account: string;
3764
3954
  bucketName: string;
3765
3955
  routing: RemoteConfig;
3766
3956
  routingJSON: string;
3767
3957
  selfEntries: HostedConfig[];
3768
3958
  self: SelfSummary | undefined;
3769
- store: IBucketStore;
3959
+ stores: LoadedStore[];
3770
3960
  structureKey: string;
3771
3961
  };
3772
- export declare function addExtraListenPort(port: number): void;
3773
- export declare function removeExtraListenPort(port: number): void;
3774
- /** Whether address:port is this server process. The ONE self test - findSelfIndexes, createApiArchives, and SourceWrapper all consult it, so "is this me" cannot disagree between the routing plan and connection building: a URL that is us on an extra listen port must never become a network client to ourselves, which is how infinite self-request loops form. */
3775
- export declare function isOwnAddress(address: string, port: number): boolean;
3962
+ /** The loaded bucket, loading it (which instantiates its stores and starts their synchronization) if needed. A bucket that does not exist on this server throws - callers never see undefined buckets. */
3963
+ export declare function requireBucket(account: string, bucketName: string): Promise<BucketState>;
3964
+ /** The store serving a request: the exact config entry the CLIENT selected, matched by equality (key order ignored) against the bucket's own entries. A match is honored even when its window has passed - the selection never validates, the store's own validation throws instead. Throws when nothing matches, listing what is available. */
3965
+ export declare function findBucketStore(account: string, bucketName: string, sourceConfig: SourceConfig | undefined): Promise<LoadedStore>;
3966
+ /** Internal (store-to-store) reads skip store selection entirely: the caller is another store whose index says this MACHINE holds the bytes - the persisted holder identity is just a URL, which cannot name a store. Whichever store's folder has the newest copy answers. */
3967
+ export declare function readBucketInternal(account: string, bucketName: string, config: {
3968
+ path: string;
3969
+ range?: {
3970
+ start: number;
3971
+ end: number;
3972
+ };
3973
+ includeTombstones?: boolean;
3974
+ }): Promise<{
3975
+ data: Buffer;
3976
+ writeTime: number;
3977
+ size: number;
3978
+ } | undefined>;
3979
+ export declare function getBucketConfig(bucket: BucketState): ArchivesConfig;
3980
+ export declare function bucketSyncStatus(bucket: BucketState): Promise<ArchivesSyncStatus>;
3981
+ export declare function bucketIndexTotals(bucket: BucketState): Promise<{
3982
+ fileCount: number;
3983
+ byteCount: number;
3984
+ sources: {
3985
+ debugName: string;
3986
+ fileCount: number;
3987
+ byteCount: number;
3988
+ }[];
3989
+ } | undefined>;
3776
3990
  /** A cached IArchives for a persisted source identity: a routing URL (hosted/backblaze) or a disk folder path - the form BlobStore's sources list stores. Configuration (valid windows, routes) decides WHEN a source should be used; for reading bytes the index says a source holds, the URL alone is enough - even for sources no longer in any config. */
3777
3991
  export declare function resolveSourceArchives(url: string): IArchives;
3778
- /** Our role in a bucket's routing config, summarized across ALL currently-valid self entries. Stored instead of a single representative HostedConfig, so nothing can accidentally use one entry's route or flags where the union is required - the standard config has the same URL twice: a routed write-shard entry plus an unrouted read-everything entry. */
3779
- export type SelfSummary = {
3780
- /** The union of the current entries' routes, with overlapping/adjacent ranges combined - which commonly collapses to a single full range, making matching trivial. */
3781
- routes: [number, number][];
3782
- public: boolean;
3783
- immutable: boolean;
3784
- noFullSync: boolean;
3785
- rawDisk: boolean;
3786
- readerDiskLimit?: number;
3787
- };
3788
- export declare function getLoadedBucket(account: string, bucketName: string): Promise<LoadedBucket | undefined>;
3789
- export declare function assertMutable(bucket: LoadedBucket, filePath: string, writeTime: number): Promise<void>;
3790
- export declare function writeBucketFile(account: string, bucketName: string, filePath: string, data: Buffer, config?: {
3992
+ export declare function getLoadedBucket(account: string, bucketName: string): Promise<BucketState | undefined>;
3993
+ /** The routing-config write path - the ONE write that cannot go through a store (it is what CREATES the bucket and its stores). Serialized per bucket: concurrent config writes would race the version check. */
3994
+ export declare function queueRoutingConfigWrite(account: string, bucketName: string, data: Buffer, config?: {
3791
3995
  lastModified?: number;
3792
- forceSetImmutable?: boolean;
3793
- internal?: boolean;
3794
3996
  }): Promise<void>;
3795
- export declare function getBucketConfig(bucket: LoadedBucket): ArchivesConfig;
3796
3997
  /** Which buckets this process currently has loaded - what a deploy successor asks its predecessor for, so it activates exactly the buckets that are actually in use. */
3797
3998
  export declare function getActiveBucketKeys(): {
3798
3999
  account: string;
@@ -3805,11 +4006,6 @@ declare module "sliftutils/storage/remoteStorage/storageServerState" {
3805
4006
  reset(): void;
3806
4007
  set(newValue: void): void;
3807
4008
  };
3808
- export type BucketDiskInfo = {
3809
- totalBytes: number;
3810
- freeBytes: number;
3811
- usedBytes: number;
3812
- };
3813
4009
  export type ServerBucketInfo = {
3814
4010
  bucketName: string;
3815
4011
  active: boolean;
@@ -3836,11 +4032,63 @@ declare module "sliftutils/storage/remoteStorage/storageServerState" {
3836
4032
  /** Loads a bucket that exists on this server's disk into memory, which starts its synchronization and window timers, and returns its live state. Nothing is written and no other server is contacted - unlike building an ArchivesChain for it, which would probe every source and could write the routing config. Already-loaded buckets just return their state. */
3837
4033
  export declare function activateBucket(account: string, bucketName: string): Promise<ActiveBucketInfo | string>;
3838
4034
  export declare function listAccountBuckets(account: string): Promise<ServerBucketInfo[]>;
3839
- export declare function deleteBucketFile(account: string, bucketName: string, filePath: string, config?: {
3840
- lastModified?: number;
3841
- internal?: boolean;
3842
- }): Promise<void>;
3843
- export declare function getLocalArchives(account: string, bucketName: string): IArchives;
4035
+ export type BucketWriteStats = {
4036
+ /** Every set call the bucket accepted */
4037
+ originalWrites: number;
4038
+ originalBytes: number;
4039
+ /** What actually reached the sources. Fast writes coalesce repeated writes to the same key, so this is lower than the original counts (and is what the disk actually did). */
4040
+ flushedWrites: number;
4041
+ flushedBytes: number;
4042
+ };
4043
+ /** Zeroes the write statistics of every bucket in the account. */
4044
+ export declare function clearAccountWriteStats(account: string): number;
4045
+ export declare function getLocalArchives(account: string, bucketName: string, sourceConfig: SourceConfig): IArchives;
4046
+
4047
+ }
4048
+
4049
+ declare module "sliftutils/storage/remoteStorage/storePlan" {
4050
+ import { RemoteConfig, HostedConfig, SourceConfig } from "../IArchives";
4051
+ export declare function findSelfIndexes(routing: RemoteConfig, account: string, bucketName: string): number[];
4052
+ export declare function selectEntryAt(entries: HostedConfig[], time: number, route?: number): HostedConfig | undefined;
4053
+ /** Our role in a bucket's routing config, summarized across ALL currently-valid self entries. Stored instead of a single representative HostedConfig, so nothing can accidentally use one entry's route or flags where the union is required - the standard config has the same URL twice: a routed write-shard entry plus an unrouted read-everything entry. */
4054
+ export type SelfSummary = {
4055
+ /** The union of the current entries' routes, with overlapping/adjacent ranges combined - which commonly collapses to a single full range, making matching trivial. */
4056
+ routes: [number, number][];
4057
+ public: boolean;
4058
+ immutable: boolean;
4059
+ noFullSync: boolean;
4060
+ rawDisk: boolean;
4061
+ readerDiskLimit?: number;
4062
+ };
4063
+ export type StoreSourceSpec = {
4064
+ sourceConfig?: SourceConfig;
4065
+ validWindow: [number, number];
4066
+ route?: [number, number];
4067
+ noFullSync?: boolean;
4068
+ };
4069
+ export type StorePlanStore = {
4070
+ routeKey: string;
4071
+ route?: [number, number];
4072
+ entries: HostedConfig[];
4073
+ rawDisk: boolean;
4074
+ readerDiskLimit?: number;
4075
+ sourceSpecs: StoreSourceSpec[];
4076
+ };
4077
+ export type StorePlan = {
4078
+ selfEntries: HostedConfig[];
4079
+ self: SelfSummary | undefined;
4080
+ stores: StorePlanStore[];
4081
+ structureKey: string;
4082
+ };
4083
+ export declare function computeStorePlan(account: string, bucketName: string, routing: RemoteConfig): StorePlan;
4084
+
4085
+ }
4086
+
4087
+ declare module "sliftutils/storage/remoteStorage/validation" {
4088
+ export declare function assertValidName(value: string, kind: string): void;
4089
+ export declare function assertValidPath(path: string): void;
4090
+ /** Method decorator: validates the well-known fields of the method's single config-object argument - account/bucketName as names, path as a path - before the method runs. Fields the config doesn't have are skipped, so it applies to every API method uniformly. prefix is deliberately NOT validated: prefixes may be empty or end with "/", both invalid for paths. */
4091
+ export declare function assertValidArgs(target: unknown, key: string, descriptor: PropertyDescriptor): void;
3844
4092
 
3845
4093
  }
3846
4094