sliftutils 1.7.71 → 1.7.73

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 (62) hide show
  1. package/index.d.ts +506 -257
  2. package/misc/https/dist/hostServer.ts.cache +5 -2
  3. package/package.json +1 -1
  4. package/spec.txt +10 -0
  5. package/storage/ArchivesDisk.d.ts +4 -13
  6. package/storage/ArchivesDisk.ts +6 -4
  7. package/storage/IArchives.d.ts +15 -10
  8. package/storage/IArchives.ts +19 -6
  9. package/storage/backblaze.d.ts +3 -9
  10. package/storage/backblaze.ts +22 -19
  11. package/storage/dist/ArchivesDisk.ts.cache +5 -2
  12. package/storage/dist/IArchives.ts.cache +17 -3
  13. package/storage/dist/backblaze.ts.cache +35 -26
  14. package/storage/remoteStorage/ArchivesRemote.d.ts +5 -9
  15. package/storage/remoteStorage/ArchivesRemote.ts +25 -20
  16. package/storage/remoteStorage/ArchivesUrl.d.ts +3 -9
  17. package/storage/remoteStorage/ArchivesUrl.ts +7 -5
  18. package/storage/remoteStorage/accessPage.tsx +4 -4
  19. package/storage/remoteStorage/accessStats.d.ts +2 -0
  20. package/storage/remoteStorage/accessStats.ts +32 -0
  21. package/storage/remoteStorage/blobStore.d.ts +193 -88
  22. package/storage/remoteStorage/blobStore.ts +535 -400
  23. package/storage/remoteStorage/bucketDisk.d.ts +18 -0
  24. package/storage/remoteStorage/bucketDisk.ts +66 -0
  25. package/storage/remoteStorage/createArchives.d.ts +4 -10
  26. package/storage/remoteStorage/createArchives.ts +103 -62
  27. package/storage/remoteStorage/dist/ArchivesRemote.ts.cache +28 -19
  28. package/storage/remoteStorage/dist/ArchivesUrl.ts.cache +7 -4
  29. package/storage/remoteStorage/dist/accessPage.tsx.cache +6 -6
  30. package/storage/remoteStorage/dist/accessStats.ts.cache +38 -3
  31. package/storage/remoteStorage/dist/blobStore.ts.cache +516 -374
  32. package/storage/remoteStorage/dist/bucketDisk.ts.cache +72 -0
  33. package/storage/remoteStorage/dist/createArchives.ts.cache +114 -57
  34. package/storage/remoteStorage/dist/intermediateSources.ts.cache +12 -12
  35. package/storage/remoteStorage/dist/remoteConfig.ts.cache +18 -2
  36. package/storage/remoteStorage/dist/serverConfig.ts.cache +116 -0
  37. package/storage/remoteStorage/dist/sourceWrapper.ts.cache +6 -5
  38. package/storage/remoteStorage/dist/sourcesList.ts.cache +5 -2
  39. package/storage/remoteStorage/dist/storageController.ts.cache +223 -190
  40. package/storage/remoteStorage/dist/storageServer.ts.cache +9 -9
  41. package/storage/remoteStorage/dist/storageServerState.ts.cache +329 -566
  42. package/storage/remoteStorage/dist/storePlan.ts.cache +149 -0
  43. package/storage/remoteStorage/dist/validation.ts.cache +40 -0
  44. package/storage/remoteStorage/grantAccessCli.ts +1 -1
  45. package/storage/remoteStorage/intermediateSources.d.ts +3 -5
  46. package/storage/remoteStorage/intermediateSources.ts +14 -16
  47. package/storage/remoteStorage/remoteConfig.d.ts +2 -2
  48. package/storage/remoteStorage/remoteConfig.ts +19 -3
  49. package/storage/remoteStorage/serverConfig.d.ts +27 -0
  50. package/storage/remoteStorage/serverConfig.ts +109 -0
  51. package/storage/remoteStorage/sourceWrapper.d.ts +3 -3
  52. package/storage/remoteStorage/sourceWrapper.ts +7 -6
  53. package/storage/remoteStorage/sourcesList.ts +3 -0
  54. package/storage/remoteStorage/storageController.d.ts +118 -36
  55. package/storage/remoteStorage/storageController.ts +166 -168
  56. package/storage/remoteStorage/storageServer.ts +1 -1
  57. package/storage/remoteStorage/storageServerState.d.ts +52 -63
  58. package/storage/remoteStorage/storageServerState.ts +340 -584
  59. package/storage/remoteStorage/storePlan.d.ts +34 -0
  60. package/storage/remoteStorage/storePlan.ts +166 -0
  61. package/storage/remoteStorage/validation.d.ts +4 -0
  62. 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;
@@ -2232,6 +2233,7 @@ declare module "sliftutils/storage/IArchives" {
2232
2233
  forceSetImmutable?: boolean;
2233
2234
  noChecks?: boolean;
2234
2235
  internal?: boolean;
2236
+ noFallbacks?: boolean;
2235
2237
  }): Promise<{
2236
2238
  writeTime: number;
2237
2239
  size: number;
@@ -2309,15 +2311,9 @@ declare module "sliftutils/storage/IArchives" {
2309
2311
  * their empty files - that is how scans learn of deletions - but nothing built on the index
2310
2312
  * ever surfaces them.
2311
2313
  */
2312
- find(prefix: string, config?: {
2313
- shallow?: boolean;
2314
- type: "files" | "folders";
2315
- }): Promise<string[]>;
2314
+ find(prefix: string, config?: FindConfig): Promise<string[]>;
2316
2315
  /** See find for the empty-file (tombstone) rule. */
2317
- findInfo(prefix: string, config?: {
2318
- shallow?: boolean;
2319
- type: "files" | "folders";
2320
- }): Promise<ArchiveFileInfo[]>;
2316
+ findInfo(prefix: string, config?: FindConfig): Promise<ArchiveFileInfo[]>;
2321
2317
  /** Only works for public buckets (private buckets are API-access only). */
2322
2318
  getURL(path: string): Promise<string>;
2323
2319
  /** The bucket's configuration, which tells whether the optional functions are supported. */
@@ -2592,7 +2588,7 @@ declare module "sliftutils/storage/TransactionStorage" {
2592
2588
  declare module "sliftutils/storage/backblaze" {
2593
2589
  /// <reference types="node" />
2594
2590
  /// <reference types="node" />
2595
- import { IArchives, ArchivesConfig, ChangesAfterConfig, ArchiveFileInfo, DelConfig, GetConfig, GetInfoConfig, SetConfig } from "./IArchives";
2591
+ import { IArchives, ArchivesConfig, ChangesAfterConfig, ArchiveFileInfo, DelConfig, FindConfig, GetConfig, GetInfoConfig, SetConfig } from "./IArchives";
2596
2592
  export declare class ArchivesBackblaze implements IArchives {
2597
2593
  private config;
2598
2594
  constructor(config: {
@@ -2631,15 +2627,9 @@ declare module "sliftutils/storage/backblaze" {
2631
2627
  writeTime: number;
2632
2628
  size: number;
2633
2629
  } | undefined>;
2634
- find(prefix: string, config?: {
2635
- shallow?: boolean;
2636
- type: "files" | "folders";
2637
- }): Promise<string[]>;
2630
+ find(prefix: string, config?: FindConfig): Promise<string[]>;
2638
2631
  getChangesAfter2(config: ChangesAfterConfig): Promise<ArchiveFileInfo[]>;
2639
- findInfo(prefix: string, config?: {
2640
- shallow?: boolean;
2641
- type: "files" | "folders";
2642
- }): Promise<{
2632
+ findInfo(prefix: string, config?: FindConfig): Promise<{
2643
2633
  path: string;
2644
2634
  createTime: number;
2645
2635
  size: number;
@@ -2896,10 +2886,12 @@ declare module "sliftutils/storage/remoteFileStorage" {
2896
2886
  declare module "sliftutils/storage/remoteStorage/ArchivesRemote" {
2897
2887
  /// <reference types="node" />
2898
2888
  /// <reference types="node" />
2899
- import { IArchives, ArchiveFileInfo, ArchivesConfig, ArchivesSyncStatus, ChangesAfterConfig, DelConfig, GetConfig, GetInfoConfig, SetConfig } from "../IArchives";
2889
+ import { IArchives, ArchiveFileInfo, ArchivesConfig, ArchivesSyncStatus, ChangesAfterConfig, DelConfig, FindConfig, GetConfig, GetInfoConfig, SourceConfig, SetConfig } from "../IArchives";
2900
2890
  export type ArchivesRemoteConfig = {
2901
2891
  url: string;
2902
2892
  waitForAccess?: boolean;
2893
+ /** 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). */
2894
+ sourceConfig: SourceConfig;
2903
2895
  };
2904
2896
  export declare function parseStorageUrl(url: string): {
2905
2897
  address: string;
@@ -2947,14 +2939,8 @@ declare module "sliftutils/storage/remoteStorage/ArchivesRemote" {
2947
2939
  writeTime: number;
2948
2940
  size: number;
2949
2941
  } | 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[]>;
2942
+ findInfo(prefix: string, config?: FindConfig): Promise<ArchiveFileInfo[]>;
2943
+ find(prefix: string, config?: FindConfig): Promise<string[]>;
2958
2944
  getChangesAfter2(config: ChangesAfterConfig): Promise<ArchiveFileInfo[]>;
2959
2945
  getConfig(): Promise<ArchivesConfig>;
2960
2946
  getSyncStatus(): Promise<ArchivesSyncStatus>;
@@ -2971,7 +2957,7 @@ declare module "sliftutils/storage/remoteStorage/ArchivesRemote" {
2971
2957
  declare module "sliftutils/storage/remoteStorage/ArchivesUrl" {
2972
2958
  /// <reference types="node" />
2973
2959
  /// <reference types="node" />
2974
- import { IArchives, ArchiveFileInfo, ArchivesConfig, ChangesAfterConfig, GetConfig, GetInfoConfig } from "../IArchives";
2960
+ import { IArchives, ArchiveFileInfo, ArchivesConfig, ChangesAfterConfig, FindConfig, GetConfig, GetInfoConfig } from "../IArchives";
2975
2961
  export declare class ArchivesUrl implements IArchives {
2976
2962
  private base;
2977
2963
  constructor(base: string);
@@ -2995,14 +2981,8 @@ declare module "sliftutils/storage/remoteStorage/ArchivesUrl" {
2995
2981
  path: string;
2996
2982
  getNextData(): Promise<Buffer | undefined>;
2997
2983
  }): 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[]>;
2984
+ find(prefix: string, config?: FindConfig): Promise<string[]>;
2985
+ findInfo(prefix: string, config?: FindConfig): Promise<ArchiveFileInfo[]>;
3006
2986
  getChangesAfter2(config: ChangesAfterConfig): Promise<ArchiveFileInfo[]>;
3007
2987
  getURL(path: string): Promise<string>;
3008
2988
  getConfig(): Promise<ArchivesConfig>;
@@ -3034,6 +3014,8 @@ declare module "sliftutils/storage/remoteStorage/accessStats" {
3034
3014
  path: string;
3035
3015
  size?: number;
3036
3016
  }): void;
3017
+ /** 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. */
3018
+ export declare function trackAccessCall(operation: string): (target: unknown, key: string, descriptor: PropertyDescriptor) => void;
3037
3019
  export declare function getAccessTotals(account: string): AccessTotals;
3038
3020
  export declare function readAccessSummaries(config: {
3039
3021
  account: string;
@@ -3048,38 +3030,47 @@ declare module "sliftutils/storage/remoteStorage/accessStats" {
3048
3030
  declare module "sliftutils/storage/remoteStorage/blobStore" {
3049
3031
  /// <reference types="node" />
3050
3032
  /// <reference types="node" />
3051
- import { IArchives, ArchiveFileInfo, ArchivesSource, ArchivesSyncStatus, ChangesAfterConfig, GetConfig, GetInfoConfig, SyncActivity } from "../IArchives";
3033
+ import { IArchives, ArchiveFileInfo, ArchivesSource, ArchivesSyncStatus, ChangesAfterConfig, FindConfig, HostedConfig, SyncActivity } from "../IArchives";
3034
+ import { ArchivesDisk } from "../ArchivesDisk";
3052
3035
  export declare const DEFAULT_FAST_WRITE_DELAY: number;
3053
3036
  export declare const WINDOW_END_FLUSH_MARGIN: number;
3054
- export type WriteConfig = {
3055
- fast?: boolean;
3056
- writeDelay?: number;
3057
- lastModified?: number;
3058
- };
3059
3037
  export type IBucketStore = {
3060
- get(fileName: string, config?: GetConfig): Promise<Buffer | undefined>;
3061
- get2(fileName: string, config?: GetConfig): Promise<{
3038
+ /** internal (store-to-store) reads answer purely from the local disk; see GetConfig.internal */
3039
+ get2(config: {
3040
+ path: string;
3041
+ range?: {
3042
+ start: number;
3043
+ end: number;
3044
+ };
3045
+ internal?: boolean;
3046
+ includeTombstones?: boolean;
3047
+ }): Promise<{
3062
3048
  data: Buffer;
3063
3049
  writeTime: number;
3064
3050
  size: number;
3065
3051
  } | undefined>;
3066
- getInternal2?(fileName: string, config?: GetConfig): Promise<{
3052
+ /** internal (store-to-store) writes go to the local disk + index with no fan-out; see SetConfig.internal */
3053
+ set(config: {
3054
+ path: string;
3067
3055
  data: Buffer;
3068
- writeTime: number;
3069
- size: number;
3070
- } | undefined>;
3071
- setInternal?(fileName: string, data: Buffer, config: {
3072
- lastModified: number;
3056
+ lastModified?: number;
3057
+ forceSetImmutable?: boolean;
3058
+ internal?: boolean;
3073
3059
  }): 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<{
3060
+ del(config: {
3061
+ path: string;
3062
+ lastModified?: number;
3063
+ internal?: boolean;
3064
+ }): Promise<void>;
3065
+ getInfo(config: {
3066
+ path: string;
3067
+ includeTombstones?: boolean;
3068
+ }): Promise<{
3077
3069
  writeTime: number;
3078
3070
  size: number;
3079
3071
  } | undefined>;
3080
- findInfo(prefix: string, config?: {
3081
- shallow?: boolean;
3082
- type?: "files" | "folders";
3072
+ findInfo(config: FindConfig & {
3073
+ prefix: string;
3083
3074
  }): Promise<ArchiveFileInfo[]>;
3084
3075
  getChangesAfter2(config: ChangesAfterConfig): Promise<ArchiveFileInfo[]>;
3085
3076
  getSyncStatus?(): Promise<ArchivesSyncStatus>;
@@ -3105,11 +3096,78 @@ declare module "sliftutils/storage/remoteStorage/blobStore" {
3105
3096
  byteCount: number;
3106
3097
  }[];
3107
3098
  }>;
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>;
3099
+ /** path/lastModified let the store reject an upload into an immutable bucket before any bytes move */
3100
+ startLargeUpload(config?: {
3101
+ path?: string;
3102
+ lastModified?: number;
3103
+ }): Promise<string>;
3104
+ appendLargeUpload(config: {
3105
+ id: string;
3106
+ data: Buffer;
3107
+ }): Promise<void>;
3108
+ finishLargeUpload(config: {
3109
+ id: string;
3110
+ path: string;
3111
+ lastModified?: number;
3112
+ }): Promise<void>;
3113
+ cancelLargeUpload(config: {
3114
+ id: string;
3115
+ }): Promise<void>;
3112
3116
  };
3117
+ /** rawDisk buckets: the disk IS the store. No index, no synchronization, no window/route/immutability validation. */
3118
+ export declare class RawDiskStore implements IBucketStore {
3119
+ private disk;
3120
+ constructor(disk: ArchivesDisk);
3121
+ get2(config: {
3122
+ path: string;
3123
+ range?: {
3124
+ start: number;
3125
+ end: number;
3126
+ };
3127
+ internal?: boolean;
3128
+ includeTombstones?: boolean;
3129
+ }): Promise<{
3130
+ data: Buffer;
3131
+ writeTime: number;
3132
+ size: number;
3133
+ } | undefined>;
3134
+ set(config: {
3135
+ path: string;
3136
+ data: Buffer;
3137
+ lastModified?: number;
3138
+ forceSetImmutable?: boolean;
3139
+ internal?: boolean;
3140
+ }): Promise<void>;
3141
+ del(config: {
3142
+ path: string;
3143
+ lastModified?: number;
3144
+ internal?: boolean;
3145
+ }): Promise<void>;
3146
+ getInfo(config: {
3147
+ path: string;
3148
+ includeTombstones?: boolean;
3149
+ }): Promise<{
3150
+ writeTime: number;
3151
+ size: number;
3152
+ } | undefined>;
3153
+ findInfo(config: FindConfig & {
3154
+ prefix: string;
3155
+ }): Promise<ArchiveFileInfo[]>;
3156
+ getChangesAfter2(config: ChangesAfterConfig): Promise<ArchiveFileInfo[]>;
3157
+ startLargeUpload(): Promise<string>;
3158
+ appendLargeUpload(config: {
3159
+ id: string;
3160
+ data: Buffer;
3161
+ }): Promise<void>;
3162
+ finishLargeUpload(config: {
3163
+ id: string;
3164
+ path: string;
3165
+ lastModified?: number;
3166
+ }): Promise<void>;
3167
+ cancelLargeUpload(config: {
3168
+ id: string;
3169
+ }): Promise<void>;
3170
+ }
3113
3171
  export type BlobSourceSpec = {
3114
3172
  identity: string;
3115
3173
  url: string;
@@ -3128,47 +3186,51 @@ declare module "sliftutils/storage/remoteStorage/blobStore" {
3128
3186
  readerDiskLimit?: number | undefined;
3129
3187
  onWriteCounted?: ((kind: "original" | "flushed", bytes: number) => void) | undefined;
3130
3188
  resolveSourceUrl?: ((url: string) => IArchives) | undefined;
3189
+ entries?: HostedConfig[] | undefined;
3131
3190
  } | 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
3191
  init: {
3153
3192
  (): Promise<void>;
3154
3193
  reset(): void;
3155
3194
  set(newValue: Promise<void>): void;
3156
3195
  };
3157
3196
  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];
3197
+ get2(config: {
3198
+ path: string;
3199
+ range?: {
3200
+ start: number;
3201
+ end: number;
3202
+ };
3203
+ internal?: boolean;
3204
+ includeTombstones?: boolean;
3205
+ }): Promise<{
3206
+ data: Buffer;
3207
+ writeTime: number;
3208
+ size: number;
3209
+ } | undefined>;
3210
+ set(config: {
3211
+ path: string;
3212
+ data: Buffer;
3213
+ lastModified?: number;
3214
+ forceSetImmutable?: boolean;
3215
+ internal?: boolean;
3171
3216
  }): Promise<void>;
3217
+ del(config: {
3218
+ path: string;
3219
+ lastModified?: number;
3220
+ internal?: boolean;
3221
+ }): Promise<void>;
3222
+ getInfo(config: {
3223
+ path: string;
3224
+ includeTombstones?: boolean;
3225
+ }): Promise<{
3226
+ writeTime: number;
3227
+ size: number;
3228
+ } | undefined>;
3229
+ findInfo(config: FindConfig & {
3230
+ prefix: string;
3231
+ }): Promise<ArchiveFileInfo[]>;
3232
+ getChangesAfter2(config: ChangesAfterConfig): Promise<ArchiveFileInfo[]>;
3233
+ getSyncStatus(): Promise<ArchivesSyncStatus>;
3172
3234
  /** The cheap always-current totals plus any in-progress background synchronization. */
3173
3235
  getSyncProgress(): {
3174
3236
  index: {
@@ -3193,7 +3255,60 @@ declare module "sliftutils/storage/remoteStorage/blobStore" {
3193
3255
  byteCount: number;
3194
3256
  }[];
3195
3257
  }>;
3258
+ /** 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). */
3259
+ updateSources(specs: BlobSourceSpec[], entries?: HostedConfig[]): void;
3260
+ /** 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. */
3261
+ rescanBase(): Promise<void>;
3262
+ /** 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. */
3263
+ boundaryScanRemote(source: IArchives, config: {
3264
+ since: number;
3265
+ route?: [number, number];
3266
+ }): Promise<void>;
3267
+ startLargeUpload(config?: {
3268
+ path?: string;
3269
+ lastModified?: number;
3270
+ }): Promise<string>;
3271
+ appendLargeUpload(config: {
3272
+ id: string;
3273
+ data: Buffer;
3274
+ }): Promise<void>;
3275
+ finishLargeUpload(config: {
3276
+ id: string;
3277
+ path: string;
3278
+ lastModified?: number;
3279
+ }): Promise<void>;
3280
+ cancelLargeUpload(config: {
3281
+ id: string;
3282
+ }): Promise<void>;
3283
+ private stopped;
3284
+ private index;
3285
+ private mem;
3286
+ private indexFileCount;
3287
+ private indexByteCount;
3288
+ private sourceFileCounts;
3289
+ private sourceByteCounts;
3290
+ private syncActivities;
3291
+ private dirty;
3292
+ private overlay;
3293
+ private sourceStates;
3294
+ private syncStarted;
3295
+ private entries;
3296
+ private sourcesList;
3297
+ private slotSourcesListIndexes;
3298
+ private slotRegistrations;
3299
+ private isLive;
3300
+ private registerSlot;
3301
+ private sourcesListIndexOfSlot;
3302
+ private slotForSourcesListIndex;
3303
+ private getEntryHolder;
3304
+ private loadIndex;
3305
+ private countEntry;
3306
+ private setIndexEntry;
3307
+ private deleteIndexEntry;
3308
+ private removeSource;
3196
3309
  private flushIndex;
3310
+ private assertMutable;
3311
+ private assertInternalWriteAccepted;
3197
3312
  private runSourceSync;
3198
3313
  private isDeadIntermediate;
3199
3314
  private scanSource;
@@ -3204,43 +3319,15 @@ declare module "sliftutils/storage/remoteStorage/blobStore" {
3204
3319
  private waitForRequiredScans;
3205
3320
  private checkMissingKey;
3206
3321
  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
3322
  /** 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>;
3323
+ private getInternal2;
3324
+ /** 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. */
3325
+ private setInternal;
3223
3326
  private cacheRead;
3224
- set(key: string, data: Buffer, config?: WriteConfig): Promise<string>;
3225
- del(key: string, config?: WriteConfig): Promise<void>;
3226
3327
  private setOrDelete;
3227
3328
  private getWritableSources;
3228
3329
  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
3330
  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
3331
  private flushOverlay;
3245
3332
  private evicting;
3246
3333
  private enforceDiskLimit;
@@ -3249,6 +3336,28 @@ declare module "sliftutils/storage/remoteStorage/blobStore" {
3249
3336
 
3250
3337
  }
3251
3338
 
3339
+ declare module "sliftutils/storage/remoteStorage/bucketDisk" {
3340
+ /// <reference types="node" />
3341
+ /// <reference types="node" />
3342
+ import { RemoteConfig } from "../IArchives";
3343
+ export declare function getBucketFolder(account: string, bucketName: string, route?: [number, number]): string;
3344
+ export declare function readRoutingFile(folder: string): Promise<Buffer | undefined>;
3345
+ export declare function readRoutingFromDisk(account: string, bucketName: string): Promise<RemoteConfig | undefined>;
3346
+ /** 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). */
3347
+ export declare function getRoutingFileResult(account: string, bucketName: string): Promise<{
3348
+ data: Buffer;
3349
+ writeTime: number;
3350
+ size: number;
3351
+ } | undefined>;
3352
+ export type BucketDiskInfo = {
3353
+ totalBytes: number;
3354
+ freeBytes: number;
3355
+ usedBytes: number;
3356
+ };
3357
+ export declare function getDiskInfo(folder: string): Promise<BucketDiskInfo>;
3358
+
3359
+ }
3360
+
3252
3361
  declare module "sliftutils/storage/remoteStorage/cliArgs" {
3253
3362
  export declare function getArg(name: string): string | undefined;
3254
3363
 
@@ -3257,11 +3366,11 @@ declare module "sliftutils/storage/remoteStorage/cliArgs" {
3257
3366
  declare module "sliftutils/storage/remoteStorage/createArchives" {
3258
3367
  /// <reference types="node" />
3259
3368
  /// <reference types="node" />
3260
- import { IArchives, RemoteConfig, RemoteConfigBase, HostedConfig, BackblazeConfig, ArchiveFileInfo, ArchivesConfig, ArchivesSyncStatus, ChangesAfterConfig, DelConfig, GetConfig, GetInfoConfig, SetConfig } from "../IArchives";
3369
+ import { IArchives, RemoteConfig, RemoteConfigBase, SourceConfig, ArchiveFileInfo, ArchivesConfig, ArchivesSyncStatus, ChangesAfterConfig, DelConfig, FindConfig, GetConfig, GetInfoConfig, SetConfig } from "../IArchives";
3261
3370
  import { ServerBucketInfo, ActiveBucketInfo } from "./storageServerState";
3262
3371
  /** 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
3372
  export { parseHostedUrl, parseBackblazeUrl, getBucketBaseUrl } from "./remoteConfig";
3264
- export declare function createApiArchives(source: HostedConfig | BackblazeConfig): IArchives;
3373
+ export declare function createApiArchives(source: SourceConfig): IArchives;
3265
3374
  export type ArchivesChainOptions = {
3266
3375
  /** 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
3376
  directConnect?: boolean;
@@ -3339,14 +3448,8 @@ declare module "sliftutils/storage/remoteStorage/createArchives" {
3339
3448
  } | undefined>;
3340
3449
  private selectCoveringSources;
3341
3450
  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[]>;
3451
+ find(prefix: string, config?: FindConfig): Promise<string[]>;
3452
+ findInfo(prefix: string, config?: FindConfig): Promise<ArchiveFileInfo[]>;
3350
3453
  getChangesAfter2(config: ChangesAfterConfig): Promise<ArchiveFileInfo[]>;
3351
3454
  getSyncStatus(): Promise<ArchivesSyncStatus>;
3352
3455
  getConfig(): Promise<ArchivesConfig>;
@@ -3440,12 +3543,11 @@ declare module "sliftutils/storage/remoteStorage/grantAccessCli" {
3440
3543
  }
3441
3544
 
3442
3545
  declare module "sliftutils/storage/remoteStorage/intermediateSources" {
3443
- import { RemoteConfig, HostedConfig, BackblazeConfig } from "../IArchives";
3546
+ import { RemoteConfig, SourceConfig } from "../IArchives";
3444
3547
  export declare const INTERMEDIATE_EXPIRE_GRACE: number;
3445
3548
  /** 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
3549
  export declare function nextIntermediateVersion(version: number): number;
3447
- type ObjectSource = HostedConfig | BackblazeConfig;
3448
- export declare function getIntermediateSources(config: RemoteConfig): ObjectSource[];
3550
+ export declare function getIntermediateSources(config: RemoteConfig): SourceConfig[];
3449
3551
  export declare function hasIntermediateSources(config: RemoteConfig): boolean;
3450
3552
  /** 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
3553
  export declare function resolveIntermediateSources(config: RemoteConfig): RemoteConfig;
@@ -3459,8 +3561,7 @@ declare module "sliftutils/storage/remoteStorage/intermediateSources" {
3459
3561
  /** Intermediates whose window ended more than INTERMEDIATE_EXPIRE_GRACE ago are removed, and the windows they split are rejoined. */
3460
3562
  export declare function expireIntermediateSources(config: RemoteConfig, now: number): RemoteConfig;
3461
3563
  /** 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 {};
3564
+ export declare function findSplitUrl(config: RemoteConfig, intermediate: SourceConfig): string | undefined;
3464
3565
 
3465
3566
  }
3466
3567
 
@@ -3471,7 +3572,7 @@ declare module "sliftutils/storage/remoteStorage/productionEnv" {
3471
3572
  declare module "sliftutils/storage/remoteStorage/remoteConfig" {
3472
3573
  /// <reference types="node" />
3473
3574
  /// <reference types="node" />
3474
- import { RemoteConfig, RemoteConfigBase, HostedConfig, BackblazeConfig, ArchiveFileInfo, ChangesAfterConfig } from "../IArchives";
3575
+ import { RemoteConfig, RemoteConfigBase, SourceConfig, ArchiveFileInfo, ChangesAfterConfig } from "../IArchives";
3475
3576
  export declare const ROUTING_FILE = "storage/storagerouting.json";
3476
3577
  /** 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
3578
  export declare function parseVariableRoute(key: string): number | undefined;
@@ -3497,21 +3598,52 @@ declare module "sliftutils/storage/remoteStorage/remoteConfig" {
3497
3598
  bucketName: string;
3498
3599
  };
3499
3600
  export declare function replaceHostedUrlPort(url: string, port: number): string;
3500
- export declare function normalizeSource(source: RemoteConfigBase): HostedConfig | BackblazeConfig;
3601
+ export declare function normalizeSource(source: RemoteConfigBase): SourceConfig;
3501
3602
  export declare function normalizeRemoteConfig(config: RemoteConfig | RemoteConfigBase): RemoteConfig;
3502
3603
  export declare function parseRoutingData(data: Buffer): RemoteConfig;
3503
3604
  export declare function serializeRemoteConfig(config: RemoteConfig): Buffer;
3504
3605
 
3505
3606
  }
3506
3607
 
3608
+ declare module "sliftutils/storage/remoteStorage/serverConfig" {
3609
+ import type { IStorage } from "../IStorage";
3610
+ import type { AccessRequest, TrustRecord } from "./storageController";
3611
+ export type StorageServerConfig = {
3612
+ domain: string;
3613
+ port: number;
3614
+ rootDomain: string;
3615
+ sshTarget: string;
3616
+ serverCommand: string;
3617
+ folder: string;
3618
+ };
3619
+ export declare function setStorageServerConfig(value: StorageServerConfig): void;
3620
+ export declare function getStorageServerConfig(): StorageServerConfig;
3621
+ export declare function getStorageServerConfigOptional(): StorageServerConfig | undefined;
3622
+ export declare function setWritesRejectedReason(reason: string | undefined): void;
3623
+ export declare function getWritesRejectedReason(): string | undefined;
3624
+ export declare function assertWritesAllowed(): void;
3625
+ export declare function getStorageFolder(): string;
3626
+ export declare function getTrust(): Promise<IStorage<TrustRecord>>;
3627
+ export declare function getRequests(): Promise<IStorage<AccessRequest[]>>;
3628
+ export declare function setTrustedMachines(config: {
3629
+ account: string;
3630
+ machineIds: string[];
3631
+ }): Promise<void>;
3632
+ export declare function addExtraListenPort(port: number): void;
3633
+ export declare function removeExtraListenPort(port: number): void;
3634
+ /** 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. */
3635
+ export declare function isOwnAddress(address: string, port: number): boolean;
3636
+
3637
+ }
3638
+
3507
3639
  declare module "sliftutils/storage/remoteStorage/sourceWrapper" {
3508
- import { IArchives, HostedConfig, BackblazeConfig, RemoteConfig } from "../IArchives";
3640
+ import { IArchives, SourceConfig, RemoteConfig } from "../IArchives";
3509
3641
  import { ArchivesUrl } from "./ArchivesUrl";
3510
3642
  export declare const RETRY_START_DELAY: number;
3511
3643
  export declare const RETRY_MAX_DELAY: number;
3512
3644
  export declare const RETRY_GROWTH = 1.5;
3513
3645
  export declare class SourceWrapper {
3514
- config: HostedConfig | BackblazeConfig;
3646
+ config: SourceConfig;
3515
3647
  private background;
3516
3648
  api?: IArchives;
3517
3649
  url?: ArchivesUrl;
@@ -3523,7 +3655,7 @@ declare module "sliftutils/storage/remoteStorage/sourceWrapper" {
3523
3655
  private constructor();
3524
3656
  /** 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
3657
  updateValidWindow(validWindow: [number, number]): void;
3526
- static create(config: HostedConfig | BackblazeConfig, options?: {
3658
+ static create(config: SourceConfig, options?: {
3527
3659
  background?: boolean;
3528
3660
  readOnly?: boolean;
3529
3661
  }): Promise<SourceWrapper>;
@@ -3588,8 +3720,8 @@ declare module "sliftutils/storage/remoteStorage/storageClientController" {
3588
3720
  declare module "sliftutils/storage/remoteStorage/storageController" {
3589
3721
  /// <reference types="node" />
3590
3722
  /// <reference types="node" />
3591
- import { ArchiveFileInfo, ArchivesConfig, ArchivesSyncStatus, ChangesAfterConfig } from "../IArchives";
3592
- import { ServerBucketInfo, ActiveBucketInfo } from "./storageServerState";
3723
+ import { ArchiveFileInfo, ArchivesConfig, ArchivesSyncStatus, FindConfig, SourceConfig } from "../IArchives";
3724
+ import { ActiveBucketInfo, ServerBucketInfo } from "./storageServerState";
3593
3725
  import { AccessTotals, AccessSummaryState } from "./accessStats";
3594
3726
  import type { SummaryEntry } from "../../treeSummary";
3595
3727
  export declare const REMOTE_STORAGE_CLASS_GUID = "RemoteStorageController-b7e42a91";
@@ -3633,58 +3765,124 @@ declare module "sliftutils/storage/remoteStorage/storageController" {
3633
3765
  machineId: string;
3634
3766
  ip: string;
3635
3767
  }>;
3636
- requestAccess: (account: string) => Promise<{
3768
+ requestAccess: (config: {
3769
+ account: string;
3770
+ }) => Promise<{
3637
3771
  machineId: string;
3638
3772
  ip: string;
3639
3773
  requestId: string;
3640
3774
  grantAccessCommand: string;
3641
3775
  }>;
3642
- getAccessState: (account: string) => Promise<AccessState>;
3643
- listRequestsForIP: (account: string, ip: string) => Promise<AccessRequest[]>;
3644
- grantAccess: (requestId: string) => Promise<TrustRecord>;
3776
+ getAccessState: (config: {
3777
+ account: string;
3778
+ }) => Promise<AccessState>;
3779
+ listRequestsForIP: (config: {
3780
+ account: string;
3781
+ ip: string;
3782
+ }) => Promise<AccessRequest[]>;
3783
+ grantAccess: (config: {
3784
+ requestId: string;
3785
+ }) => Promise<TrustRecord>;
3645
3786
  adminListActiveBuckets: () => Promise<{
3646
3787
  account: string;
3647
3788
  bucketName: string;
3648
3789
  }[]>;
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<{
3790
+ adminListRequests: (config: {
3791
+ ip: string;
3792
+ }) => Promise<AccessRequest[]>;
3793
+ adminGrantAccess: (config: {
3794
+ requestId: string;
3795
+ }) => Promise<TrustRecord>;
3796
+ get2: (config: {
3797
+ account: string;
3798
+ bucketName: string;
3799
+ path: string;
3800
+ sourceConfig: SourceConfig;
3801
+ range?: {
3802
+ start: number;
3803
+ end: number;
3804
+ };
3805
+ internal?: boolean;
3806
+ includeTombstones?: boolean;
3807
+ }) => Promise<{
3659
3808
  data: Buffer;
3660
3809
  writeTime: number;
3661
3810
  size: number;
3662
3811
  } | 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<{
3812
+ set: (config: {
3813
+ account: string;
3814
+ bucketName: string;
3815
+ path: string;
3816
+ data: Buffer;
3817
+ sourceConfig: SourceConfig;
3818
+ lastModified?: number;
3819
+ forceSetImmutable?: boolean;
3820
+ internal?: boolean;
3821
+ }) => Promise<void>;
3822
+ del: (config: {
3823
+ account: string;
3824
+ bucketName: string;
3825
+ path: string;
3826
+ sourceConfig: SourceConfig;
3827
+ lastModified?: number;
3828
+ internal?: boolean;
3829
+ }) => Promise<void>;
3830
+ getInfo: (config: {
3831
+ account: string;
3832
+ bucketName: string;
3833
+ path: string;
3834
+ sourceConfig: SourceConfig;
3835
+ includeTombstones?: boolean;
3836
+ }) => Promise<{
3666
3837
  writeTime: number;
3667
3838
  size: number;
3668
3839
  } | undefined>;
3669
- findInfo: (account: string, bucketName: string, prefix: string, config?: {
3670
- shallow?: boolean;
3671
- type?: "files" | "folders";
3840
+ findInfo: (config: FindConfig & {
3841
+ account: string;
3842
+ bucketName: string;
3843
+ prefix: string;
3844
+ sourceConfig: SourceConfig;
3672
3845
  }) => 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<{
3846
+ getChangesAfter2: (config: {
3847
+ account: string;
3848
+ bucketName: string;
3849
+ sourceConfig: SourceConfig;
3850
+ time: number;
3851
+ routes?: [number, number][];
3852
+ }) => Promise<ArchiveFileInfo[]>;
3853
+ getArchivesConfig: (config: {
3854
+ account: string;
3855
+ bucketName: string;
3856
+ }) => Promise<ArchivesConfig>;
3857
+ listBuckets: (config: {
3858
+ account: string;
3859
+ }) => Promise<ServerBucketInfo[]>;
3860
+ getActiveBucket: (config: {
3861
+ account: string;
3862
+ bucketName: string;
3863
+ }) => Promise<ActiveBucketInfo | string>;
3864
+ activateBucket: (config: {
3865
+ account: string;
3866
+ bucketName: string;
3867
+ }) => Promise<ActiveBucketInfo | string>;
3868
+ clearWriteStats: (config: {
3869
+ account: string;
3870
+ }) => Promise<{
3679
3871
  clearedBuckets: number;
3680
3872
  }>;
3681
- getAccessStats: (account: string) => Promise<AccessTotals>;
3682
- getAccessSummaries: (account: string, config: {
3873
+ getAccessStats: (config: {
3874
+ account: string;
3875
+ }) => Promise<AccessTotals>;
3876
+ getAccessSummaries: (config: {
3877
+ account: string;
3683
3878
  operation: string;
3684
3879
  maxCount: number;
3685
3880
  weightBySize?: boolean;
3686
3881
  }) => Promise<SummaryEntry<AccessSummaryState>[]>;
3687
- getIndexInfo: (account: string, bucketName: string) => Promise<{
3882
+ getIndexInfo: (config: {
3883
+ account: string;
3884
+ bucketName: string;
3885
+ }) => Promise<{
3688
3886
  fileCount: number;
3689
3887
  byteCount: number;
3690
3888
  sources: {
@@ -3693,11 +3891,27 @@ declare module "sliftutils/storage/remoteStorage/storageController" {
3693
3891
  byteCount: number;
3694
3892
  }[];
3695
3893
  } | 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>;
3894
+ getSyncStatus: (config: {
3895
+ account: string;
3896
+ bucketName: string;
3897
+ }) => Promise<ArchivesSyncStatus>;
3898
+ startLargeFile: (config: {
3899
+ account: string;
3900
+ bucketName: string;
3901
+ path: string;
3902
+ sourceConfig: SourceConfig;
3903
+ lastModified?: number;
3904
+ }) => Promise<string>;
3905
+ uploadPart: (config: {
3906
+ uploadId: string;
3907
+ data: Buffer;
3908
+ }) => Promise<void>;
3909
+ finishLargeFile: (config: {
3910
+ uploadId: string;
3911
+ }) => Promise<void>;
3912
+ cancelLargeFile: (config: {
3913
+ uploadId: string;
3914
+ }) => Promise<void>;
3701
3915
  httpEntry: (config?: {
3702
3916
  requireCalls?: string[];
3703
3917
  cacheTime?: number;
@@ -3726,73 +3940,61 @@ declare module "sliftutils/storage/remoteStorage/storageServerState" {
3726
3940
  /// <reference types="node" />
3727
3941
  /// <reference types="node" />
3728
3942
  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;
3943
+ import { RemoteConfig, HostedConfig, SourceConfig, IArchives, ArchivesConfig, ArchivesSyncStatus } from "../IArchives";
3944
+ import { BucketDiskInfo } from "./bucketDisk";
3945
+ import { SelfSummary } from "./storePlan";
3946
+ export type LoadedStore = {
3947
+ routeKey: string;
3948
+ route?: [number, number];
3949
+ entries: HostedConfig[];
3738
3950
  folder: string;
3951
+ store: IBucketStore;
3739
3952
  };
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 = {
3953
+ export type BucketState = {
3763
3954
  account: string;
3764
3955
  bucketName: string;
3765
3956
  routing: RemoteConfig;
3766
3957
  routingJSON: string;
3767
3958
  selfEntries: HostedConfig[];
3768
3959
  self: SelfSummary | undefined;
3769
- store: IBucketStore;
3960
+ stores: LoadedStore[];
3770
3961
  structureKey: string;
3771
3962
  };
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;
3963
+ /** 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. */
3964
+ export declare function requireBucket(account: string, bucketName: string): Promise<BucketState>;
3965
+ /** 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. */
3966
+ export declare function findBucketStore(account: string, bucketName: string, sourceConfig: SourceConfig | undefined): Promise<LoadedStore>;
3967
+ /** 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. */
3968
+ export declare function readBucketInternal(account: string, bucketName: string, config: {
3969
+ path: string;
3970
+ range?: {
3971
+ start: number;
3972
+ end: number;
3973
+ };
3974
+ includeTombstones?: boolean;
3975
+ }): Promise<{
3976
+ data: Buffer;
3977
+ writeTime: number;
3978
+ size: number;
3979
+ } | undefined>;
3980
+ export declare function getBucketConfig(bucket: BucketState): ArchivesConfig;
3981
+ export declare function bucketSyncStatus(bucket: BucketState): Promise<ArchivesSyncStatus>;
3982
+ export declare function bucketIndexTotals(bucket: BucketState): Promise<{
3983
+ fileCount: number;
3984
+ byteCount: number;
3985
+ sources: {
3986
+ debugName: string;
3987
+ fileCount: number;
3988
+ byteCount: number;
3989
+ }[];
3990
+ } | undefined>;
3776
3991
  /** 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
3992
  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?: {
3993
+ export declare function getLoadedBucket(account: string, bucketName: string): Promise<BucketState | undefined>;
3994
+ /** 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. */
3995
+ export declare function queueRoutingConfigWrite(account: string, bucketName: string, data: Buffer, config?: {
3791
3996
  lastModified?: number;
3792
- forceSetImmutable?: boolean;
3793
- internal?: boolean;
3794
3997
  }): Promise<void>;
3795
- export declare function getBucketConfig(bucket: LoadedBucket): ArchivesConfig;
3796
3998
  /** 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
3999
  export declare function getActiveBucketKeys(): {
3798
4000
  account: string;
@@ -3805,11 +4007,6 @@ declare module "sliftutils/storage/remoteStorage/storageServerState" {
3805
4007
  reset(): void;
3806
4008
  set(newValue: void): void;
3807
4009
  };
3808
- export type BucketDiskInfo = {
3809
- totalBytes: number;
3810
- freeBytes: number;
3811
- usedBytes: number;
3812
- };
3813
4010
  export type ServerBucketInfo = {
3814
4011
  bucketName: string;
3815
4012
  active: boolean;
@@ -3836,11 +4033,63 @@ declare module "sliftutils/storage/remoteStorage/storageServerState" {
3836
4033
  /** 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
4034
  export declare function activateBucket(account: string, bucketName: string): Promise<ActiveBucketInfo | string>;
3838
4035
  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;
4036
+ export type BucketWriteStats = {
4037
+ /** Every set call the bucket accepted */
4038
+ originalWrites: number;
4039
+ originalBytes: number;
4040
+ /** 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). */
4041
+ flushedWrites: number;
4042
+ flushedBytes: number;
4043
+ };
4044
+ /** Zeroes the write statistics of every bucket in the account. */
4045
+ export declare function clearAccountWriteStats(account: string): number;
4046
+ export declare function getLocalArchives(account: string, bucketName: string, sourceConfig: SourceConfig): IArchives;
4047
+
4048
+ }
4049
+
4050
+ declare module "sliftutils/storage/remoteStorage/storePlan" {
4051
+ import { RemoteConfig, HostedConfig, SourceConfig } from "../IArchives";
4052
+ export declare function findSelfIndexes(routing: RemoteConfig, account: string, bucketName: string): number[];
4053
+ export declare function selectEntryAt(entries: HostedConfig[], time: number, route?: number): HostedConfig | undefined;
4054
+ /** 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. */
4055
+ export type SelfSummary = {
4056
+ /** The union of the current entries' routes, with overlapping/adjacent ranges combined - which commonly collapses to a single full range, making matching trivial. */
4057
+ routes: [number, number][];
4058
+ public: boolean;
4059
+ immutable: boolean;
4060
+ noFullSync: boolean;
4061
+ rawDisk: boolean;
4062
+ readerDiskLimit?: number;
4063
+ };
4064
+ export type StoreSourceSpec = {
4065
+ sourceConfig?: SourceConfig;
4066
+ validWindow: [number, number];
4067
+ route?: [number, number];
4068
+ noFullSync?: boolean;
4069
+ };
4070
+ export type StorePlanStore = {
4071
+ routeKey: string;
4072
+ route?: [number, number];
4073
+ entries: HostedConfig[];
4074
+ rawDisk: boolean;
4075
+ readerDiskLimit?: number;
4076
+ sourceSpecs: StoreSourceSpec[];
4077
+ };
4078
+ export type StorePlan = {
4079
+ selfEntries: HostedConfig[];
4080
+ self: SelfSummary | undefined;
4081
+ stores: StorePlanStore[];
4082
+ structureKey: string;
4083
+ };
4084
+ export declare function computeStorePlan(account: string, bucketName: string, routing: RemoteConfig): StorePlan;
4085
+
4086
+ }
4087
+
4088
+ declare module "sliftutils/storage/remoteStorage/validation" {
4089
+ export declare function assertValidName(value: string, kind: string): void;
4090
+ export declare function assertValidPath(path: string): void;
4091
+ /** 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. */
4092
+ export declare function assertValidArgs(target: unknown, key: string, descriptor: PropertyDescriptor): void;
3844
4093
 
3845
4094
  }
3846
4095