sliftutils 1.7.10 → 1.7.11

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 (83) hide show
  1. package/examplestorage/exampleserver.ts +0 -2
  2. package/index.d.ts +290 -54
  3. package/misc/dist/environment.ts.cache +3 -3
  4. package/misc/dist/getSecret.ts.cache +139 -0
  5. package/misc/dist/strings.ts.cache +13 -0
  6. package/misc/dist/zip.ts.cache +9 -0
  7. package/misc/getSecret.ts +38 -16
  8. package/misc/https/cloudflareHelpers.d.ts +19 -0
  9. package/misc/https/cloudflareHelpers.ts +69 -0
  10. package/misc/https/dist/certs.ts.cache +115 -179
  11. package/misc/https/dist/cloudflareHelpers.ts.cache +68 -0
  12. package/misc/https/dist/dns.ts.cache +112 -100
  13. package/misc/https/dist/hostServer.ts.cache +130 -0
  14. package/misc/https/dist/httpsCerts.ts.cache +24 -5
  15. package/misc/https/dist/persistentLocalStorage.ts.cache +3 -2
  16. package/misc/https/dns.d.ts +41 -12
  17. package/misc/https/dns.ts +104 -101
  18. package/misc/https/hostServer.d.ts +0 -6
  19. package/misc/https/hostServer.ts +9 -5
  20. package/package.json +2 -2
  21. package/render-utils/dist/observer.tsx.cache +3 -2
  22. package/spec.txt +0 -90
  23. package/storage/ArchivesDisk.d.ts +2 -0
  24. package/storage/ArchivesDisk.ts +7 -3
  25. package/storage/BulkDatabase2/dist/BulkDatabaseBase.ts.cache +1242 -0
  26. package/storage/BulkDatabase2/dist/BulkDatabaseFormat.ts.cache +513 -0
  27. package/storage/BulkDatabase2/dist/BulkDatabaseMerge.ts.cache +352 -0
  28. package/storage/BulkDatabase2/dist/BulkDatabaseReader.ts.cache +306 -0
  29. package/storage/BulkDatabase2/dist/LoadedIndex.ts.cache +435 -0
  30. package/storage/BulkDatabase2/dist/WriteOverlay.ts.cache +58 -0
  31. package/storage/BulkDatabase2/dist/blockCache.ts.cache +179 -0
  32. package/storage/BulkDatabase2/dist/mergeLock.ts.cache +149 -0
  33. package/storage/BulkDatabase2/dist/mergeMarkers.ts.cache +101 -0
  34. package/storage/BulkDatabase2/dist/streamLog.ts.cache +227 -0
  35. package/storage/BulkDatabase2/dist/syncClient.ts.cache +104 -0
  36. package/storage/IArchives.d.ts +47 -15
  37. package/storage/IArchives.ts +86 -31
  38. package/storage/backblaze.d.ts +15 -0
  39. package/storage/backblaze.ts +199 -79
  40. package/storage/dist/ArchivesDisk.ts.cache +373 -0
  41. package/storage/dist/FileFolderAPI.tsx.cache +508 -65
  42. package/storage/dist/IArchives.ts.cache +44 -0
  43. package/storage/dist/IndexedDBFileFolderAPI.ts.cache +8 -2
  44. package/storage/dist/JSONStorage.ts.cache +6 -2
  45. package/storage/dist/PendingManager.tsx.cache +1 -1
  46. package/storage/dist/TransactionStorage.ts.cache +231 -92
  47. package/storage/dist/backblaze.ts.cache +797 -0
  48. package/storage/dist/fileSystemPointer.ts.cache +36 -3
  49. package/storage/dist/remoteFileStorage.ts.cache +495 -0
  50. package/storage/remoteStorage/ArchivesRemote.d.ts +5 -1
  51. package/storage/remoteStorage/ArchivesRemote.ts +28 -8
  52. package/storage/remoteStorage/ArchivesUrl.d.ts +2 -0
  53. package/storage/remoteStorage/ArchivesUrl.ts +16 -4
  54. package/storage/remoteStorage/blobStore.d.ts +52 -3
  55. package/storage/remoteStorage/blobStore.ts +414 -101
  56. package/storage/remoteStorage/createArchives.d.ts +31 -10
  57. package/storage/remoteStorage/createArchives.ts +470 -253
  58. package/storage/remoteStorage/dist/ArchivesRemote.ts.cache +197 -0
  59. package/storage/remoteStorage/dist/ArchivesUrl.ts.cache +85 -0
  60. package/storage/remoteStorage/dist/accessPage.tsx.cache +222 -0
  61. package/storage/remoteStorage/dist/blobStore.ts.cache +837 -0
  62. package/storage/remoteStorage/dist/cliArgs.ts.cache +16 -0
  63. package/storage/remoteStorage/dist/createArchives.ts.cache +610 -0
  64. package/storage/remoteStorage/dist/remoteConfig.ts.cache +206 -0
  65. package/storage/remoteStorage/dist/sourceWrapper.ts.cache +223 -0
  66. package/storage/remoteStorage/dist/storageController.ts.cache +500 -0
  67. package/storage/remoteStorage/dist/storageServer.ts.cache +106 -0
  68. package/storage/remoteStorage/dist/storageServerCli.ts.cache +39 -0
  69. package/storage/remoteStorage/dist/storageServerState.ts.cache +518 -0
  70. package/storage/remoteStorage/remoteConfig.d.ts +8 -0
  71. package/storage/remoteStorage/remoteConfig.ts +105 -5
  72. package/storage/remoteStorage/sourceWrapper.d.ts +41 -0
  73. package/storage/remoteStorage/sourceWrapper.ts +223 -0
  74. package/storage/remoteStorage/spec.md +31 -0
  75. package/storage/remoteStorage/storageController.d.ts +11 -0
  76. package/storage/remoteStorage/storageController.ts +109 -40
  77. package/storage/remoteStorage/storageServer.d.ts +0 -5
  78. package/storage/remoteStorage/storageServer.ts +17 -10
  79. package/storage/remoteStorage/storageServerCli.ts +0 -3
  80. package/storage/remoteStorage/storageServerState.d.ts +7 -1
  81. package/storage/remoteStorage/storageServerState.ts +230 -48
  82. package/testsite/server.ts +0 -2
  83. package/yarn.lock +11 -6
@@ -1,5 +1,4 @@
1
1
  process.env.NODE_ENV = "production";
2
- import os from "os";
3
2
  import path from "path";
4
3
  import { SocketFunction } from "socket-function/SocketFunction";
5
4
  import { RequireController } from "socket-function/require/RequireController";
@@ -31,7 +30,6 @@ async function main() {
31
30
  await hostServer({
32
31
  domain: DOMAIN,
33
32
  port: PORT,
34
- cloudflareApiToken: { path: os.homedir() + "/vidgridweb.com.key" },
35
33
  setDNSRecord: true,
36
34
  });
37
35
  console.log(`Storage example site running at https://${DOMAIN}:${PORT}`);
package/index.d.ts CHANGED
@@ -136,29 +136,81 @@ declare module "sliftutils/misc/https/certs" {
136
136
 
137
137
  }
138
138
 
139
+ declare module "sliftutils/misc/https/cloudflareHelpers" {
140
+ /// <reference types="node" />
141
+ /// <reference types="node" />
142
+ export type CloudflareCreds = {
143
+ key: string;
144
+ /** Set for legacy global API keys, which auth via X-Auth-Email/X-Auth-Key. Absent for API tokens, which auth via Authorization: Bearer. */
145
+ email?: string;
146
+ };
147
+ export declare const getCloudflareCreds: {
148
+ (): Promise<CloudflareCreds>;
149
+ reset(): void;
150
+ set(newValue: Promise<CloudflareCreds>): void;
151
+ };
152
+ export declare function cloudflareGETCall<T>(path: string, params?: {
153
+ [key: string]: string;
154
+ }): Promise<T>;
155
+ export declare function cloudflarePOSTCall<T>(path: string, params: {
156
+ [key: string]: unknown;
157
+ }): Promise<T>;
158
+ export declare function cloudflareCall<T>(path: string, payload: Buffer, method: string): Promise<T>;
159
+
160
+ }
161
+
139
162
  declare module "sliftutils/misc/https/dns" {
163
+ /** Parses our tag back out; 0 (i.e. always stale) when it's absent or unparseable. */
164
+ export declare function freshnessTime(comment?: string): number;
165
+ export declare const hasDNSWritePermissions: {
166
+ (): Promise<boolean>;
167
+ reset(): void;
168
+ set(newValue: Promise<boolean>): void;
169
+ };
170
+ export declare const getZoneId: {
171
+ (key: string): Promise<string>;
172
+ clear(key: string): void;
173
+ clearAll(): void;
174
+ forceSet(key: string, value: Promise<string>): void;
175
+ getAllKeys(): string[];
176
+ get(key: string): Promise<string> | undefined;
177
+ };
140
178
  export declare function getRecordsRaw(type: string, key: string): Promise<{
141
179
  id: string;
142
180
  type: string;
143
181
  name: string;
144
182
  content: string;
145
183
  proxied: boolean;
184
+ modified_on: string;
185
+ comment?: string | undefined;
146
186
  }[]>;
187
+ /** Cloudflare's batch endpoint applies deletes, then patches, then posts in a single database
188
+ * transaction. We route edits (patches) through here because the standalone PATCH/PUT verbs
189
+ * aren't usable in our setup, and because it lets "remove others + assert target" happen
190
+ * without a window where the name resolves to nothing. */
191
+ export declare function batchRecords(zoneId: string, batch: {
192
+ deletes?: {
193
+ id: string;
194
+ }[];
195
+ patches?: {
196
+ id: string;
197
+ comment?: string;
198
+ }[];
199
+ posts?: {
200
+ type: string;
201
+ name: string;
202
+ content: string;
203
+ ttl: number;
204
+ proxied: boolean;
205
+ comment?: string;
206
+ }[];
207
+ }): Promise<void>;
147
208
  export declare function getRecords(type: string, key: string): Promise<string[]>;
148
209
  export declare function deleteRecord(type: string, key: string, value: string): Promise<void>;
149
- /** Removes all existing records (unless the record is already present) */
150
- export declare function setRecord(type: string, key: string, value: string, proxied?: "proxied"): Promise<void>;
210
+ /** Removes all existing records (unless the record is already present and fresh) */
211
+ export declare function setRecord(type: string, key: string, value: string, proxied?: "proxied", staleAfter?: number): Promise<void>;
151
212
  /** Keeps existing records */
152
- export declare function addRecord(type: string, key: string, value: string, proxied?: "proxied"): Promise<void>;
153
- /** Provide Cloudflare credentials directly instead of relying on ./cloudflare.json. Exactly one of
154
- * key (the API token itself) or path (a file to read it from) — TypeScript rejects both/neither. */
155
- export declare function setCloudflareCredentials(config: {
156
- value: {
157
- key: string;
158
- } | {
159
- path: string;
160
- };
161
- }): void;
213
+ export declare function addRecord(type: string, key: string, value: string, proxied?: "proxied", staleAfter?: number): Promise<void>;
162
214
 
163
215
  }
164
216
 
@@ -167,12 +219,6 @@ declare module "sliftutils/misc/https/hostServer" {
167
219
  /** Full domain to host on (e.g. "testsite.example.com"). The HTTPS cert is created for this domain and *.domain, so using a subdomain never touches the root domain (beyond its _acme-challenge TXT record). */
168
220
  domain: string;
169
221
  port: number;
170
- /** Cloudflare API token: either the token string ({ key }) or a path to a file containing it ({ path }). Required — pass { path: "./cloudflare.json" } explicitly for the on-disk file. */
171
- cloudflareApiToken: {
172
- key: string;
173
- } | {
174
- path: string;
175
- };
176
222
  /** Creates an unproxied A record pointing domain at this machine (publicIp, or our detected external IP) */
177
223
  setDNSRecord?: boolean;
178
224
  publicIp?: string;
@@ -822,6 +868,7 @@ declare module "sliftutils/storage/ArchivesDisk" {
822
868
  };
823
869
  getDebugName(): string;
824
870
  getConfig(): Promise<ArchivesConfig>;
871
+ hasWriteAccess(): Promise<boolean>;
825
872
  private filePath;
826
873
  set(key: string, data: Buffer, config?: {
827
874
  lastModified?: number;
@@ -841,6 +888,7 @@ declare module "sliftutils/storage/ArchivesDisk" {
841
888
  }): Promise<{
842
889
  data: Buffer;
843
890
  writeTime: number;
891
+ size: number;
844
892
  } | undefined>;
845
893
  getInfo(key: string): Promise<{
846
894
  writeTime: number;
@@ -2049,6 +2097,7 @@ declare module "sliftutils/storage/IArchives" {
2049
2097
  /// <reference types="node" />
2050
2098
  /// <reference types="node" />
2051
2099
  export declare const MAX_LAST_MODIFIED_FUTURE: number;
2100
+ export declare const IMMUTABLE_CACHE_TIME: number;
2052
2101
  export declare function assertValidLastModified(lastModified: number): void;
2053
2102
  export type RemoteConfig = {
2054
2103
  version?: number;
@@ -2067,8 +2116,14 @@ declare module "sliftutils/storage/IArchives" {
2067
2116
  */
2068
2117
  export type RemoteConfigBase = string | HostedConfig | BackblazeConfig;
2069
2118
  export type CommonConfig = {
2070
- /** The default options for the first config in a list is DEFAULT_BASE_SYNC_OPTIONS. The rest default to DEFAULT_SYNC_OPTIONS. */
2071
- syncOptions?: SyncOptions;
2119
+ /** 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. */
2120
+ noFullSync?: boolean;
2121
+ /** Bytes of read-cache this server's disk may hold; least-recently-used files are deleted from disk to stay under it (only ever when another source verifiably holds the file - the only copy is never deleted). Requires noFullSync (a full copy can't be bounded). */
2122
+ readerDiskLimit?: number;
2123
+ /** The write times ([startMs, endMs]) this source is valid for (see ArchivesSource.validWindow for the synchronization semantics). Required on object configs: configuration changes must be SCHEDULED (a new source becomes valid at a future time while the old one's window ends), not flipped instantly. Plain URL-string sources default to FULL_VALID_WINDOW - once you're writing object configs, you're doing something complicated enough to think about when things change. */
2124
+ validWindow: [number, number];
2125
+ /** Sharding: the fraction of the key space this source handles, as [start, end) over [0, 1) (keys are routed by getRoute in remoteConfig.ts). Defaults to FULL_ROUTE (unsharded). At every point in time the sources' routes must fully cover [0, 1), or some keys could never be read. */
2126
+ route?: [number, number];
2072
2127
  };
2073
2128
  export type HostedConfig = CommonConfig & {
2074
2129
  type: "remote";
@@ -2083,34 +2138,56 @@ declare module "sliftutils/storage/IArchives" {
2083
2138
  export type BackblazeConfig = CommonConfig & {
2084
2139
  type: "backblaze";
2085
2140
  url: string;
2086
- bucketName: string;
2087
2141
  public?: boolean;
2088
2142
  immutable?: boolean;
2089
2143
  };
2090
- export declare const DEFAULT_BASE_SYNC_OPTIONS: SyncOptions;
2091
- export declare const DEFAULT_SYNC_OPTIONS: SyncOptions;
2144
+ export declare const FULL_VALID_WINDOW: [number, number];
2092
2145
  export type ArchiveFileInfo = {
2093
2146
  path: string;
2094
2147
  createTime: number;
2095
2148
  size: number;
2096
2149
  };
2150
+ export type SyncActivity = {
2151
+ type: "metadataScan" | "fullSync";
2152
+ sourceDebugName: string;
2153
+ startTime: number;
2154
+ doneFiles?: number;
2155
+ totalFiles?: number;
2156
+ doneBytes?: number;
2157
+ totalBytes?: number;
2158
+ };
2097
2159
  export type ArchivesConfig = {
2098
2160
  supportsChangesAfter?: boolean;
2099
- };
2100
- export type SyncOptions = {
2101
- copyFiles?: boolean;
2102
- noWriteBack?: boolean;
2103
- cacheReads?: boolean;
2104
- validWindow: [number, number];
2105
- required?: boolean;
2161
+ remoteConfig?: RemoteConfig;
2162
+ index?: {
2163
+ fileCount: number;
2164
+ byteCount: number;
2165
+ };
2166
+ indexSources?: {
2167
+ debugName: string;
2168
+ fileCount: number;
2169
+ byteCount: number;
2170
+ }[];
2171
+ readerDiskLimit?: number;
2172
+ syncing?: SyncActivity[];
2106
2173
  };
2107
2174
  export type ArchivesSource = {
2108
2175
  source: IArchives;
2109
- options: SyncOptions;
2110
- };
2176
+ validWindow: [number, number];
2177
+ route?: [number, number];
2178
+ noFullSync?: boolean;
2179
+ };
2180
+ export declare const WRITE_PAST_WINDOW_GRACE: number;
2181
+ export declare const STORAGE_WRONG_VALID_WINDOW = "REMOTE_STORAGE_WRONG_VALID_WINDOW_a7c1f04e";
2182
+ export declare const STORAGE_WRONG_ROUTE = "REMOTE_STORAGE_WRONG_ROUTE_c94d2e17";
2183
+ export declare const FULL_ROUTE: [number, number];
2184
+ export declare const VARIABLE_SHARD = "VARIABLE_SHARD_f0234jfah08fgyhfgyssdds83nmp";
2185
+ export declare function windowAcceptsWrites(validWindow: [number, number] | undefined): boolean;
2111
2186
  export type ArchivesSyncSourceStatus = {
2112
2187
  debugName: string;
2113
- options: SyncOptions;
2188
+ validWindow: [number, number];
2189
+ route?: [number, number];
2190
+ noFullSync?: boolean;
2114
2191
  supportsChangesAfter: boolean;
2115
2192
  initialScanComplete: boolean;
2116
2193
  scannedCount: number;
@@ -2122,13 +2199,15 @@ declare module "sliftutils/storage/IArchives" {
2122
2199
  };
2123
2200
  export interface IArchives {
2124
2201
  getDebugName(): string;
2202
+ /** Whether writes would be accepted (credentials exist, the account trusts this machine, etc).
2203
+ * Checked without writing anything. */
2204
+ hasWriteAccess(): Promise<boolean>;
2125
2205
  get(fileName: string, config?: {
2126
2206
  range?: {
2127
2207
  start: number;
2128
2208
  end: number;
2129
2209
  };
2130
2210
  }): Promise<Buffer | undefined>;
2131
- /** Like get, but also returns the last-write time of the file. get just calls get2. */
2132
2211
  get2(fileName: string, config?: {
2133
2212
  range?: {
2134
2213
  start: number;
@@ -2137,6 +2216,7 @@ declare module "sliftutils/storage/IArchives" {
2137
2216
  }): Promise<{
2138
2217
  data: Buffer;
2139
2218
  writeTime: number;
2219
+ size: number;
2140
2220
  } | undefined>;
2141
2221
  /**
2142
2222
  * lastModified stamps the write with that last-write time instead of now. If it is OLDER than
@@ -2444,6 +2524,7 @@ declare module "sliftutils/storage/backblaze" {
2444
2524
  public?: boolean;
2445
2525
  immutable?: boolean;
2446
2526
  cacheTime?: number;
2527
+ allowedOrigins?: string[];
2447
2528
  });
2448
2529
  private bucketName;
2449
2530
  private bucketId;
@@ -2452,6 +2533,7 @@ declare module "sliftutils/storage/backblaze" {
2452
2533
  private log;
2453
2534
  getDebugName(): string;
2454
2535
  private getBucketAPI;
2536
+ private currentReset;
2455
2537
  private last503Reset;
2456
2538
  private apiRetryLogic;
2457
2539
  get(fileName: string, config?: {
@@ -2469,8 +2551,10 @@ declare module "sliftutils/storage/backblaze" {
2469
2551
  }): Promise<{
2470
2552
  data: Buffer;
2471
2553
  writeTime: number;
2554
+ size: number;
2472
2555
  } | undefined>;
2473
2556
  getConfig(): Promise<ArchivesConfig>;
2557
+ hasWriteAccess(): Promise<boolean>;
2474
2558
  set(fileName: string, data: Buffer, config?: {
2475
2559
  lastModified?: number;
2476
2560
  }): Promise<void>;
@@ -2496,6 +2580,17 @@ declare module "sliftutils/storage/backblaze" {
2496
2580
  size: number;
2497
2581
  }[]>;
2498
2582
  assertPathValid(path: string): Promise<void>;
2583
+ move(config: {
2584
+ path: string;
2585
+ target: IArchives;
2586
+ targetPath: string;
2587
+ copyInstead?: boolean;
2588
+ }): Promise<void>;
2589
+ copy(config: {
2590
+ path: string;
2591
+ target: IArchives;
2592
+ targetPath: string;
2593
+ }): Promise<void>;
2499
2594
  getURL(path: string): Promise<string>;
2500
2595
  getDownloadAuthorization(config: {
2501
2596
  fileNamePrefix?: string;
@@ -2784,6 +2879,8 @@ declare module "sliftutils/storage/remoteStorage/ArchivesRemote" {
2784
2879
  private controller;
2785
2880
  private lastDeniedLog;
2786
2881
  getDebugName(): string;
2882
+ isConnected(): boolean;
2883
+ ping(): Promise<void>;
2787
2884
  private authenticate;
2788
2885
  private callAuthed;
2789
2886
  waitingForAccess(): Promise<{
@@ -2791,7 +2888,8 @@ declare module "sliftutils/storage/remoteStorage/ArchivesRemote" {
2791
2888
  machineId: string;
2792
2889
  ip: string;
2793
2890
  } | undefined>;
2794
- private onAccessDenied;
2891
+ hasWriteAccess(): Promise<boolean>;
2892
+ private registerAccessRequest;
2795
2893
  private call;
2796
2894
  get(fileName: string, config?: {
2797
2895
  range?: {
@@ -2807,6 +2905,7 @@ declare module "sliftutils/storage/remoteStorage/ArchivesRemote" {
2807
2905
  }): Promise<{
2808
2906
  data: Buffer;
2809
2907
  writeTime: number;
2908
+ size: number;
2810
2909
  } | undefined>;
2811
2910
  set(fileName: string, data: Buffer, config?: {
2812
2911
  lastModified?: number;
@@ -2859,6 +2958,7 @@ declare module "sliftutils/storage/remoteStorage/ArchivesUrl" {
2859
2958
  }): Promise<{
2860
2959
  data: Buffer;
2861
2960
  writeTime: number;
2961
+ size: number;
2862
2962
  } | undefined>;
2863
2963
  getInfo(fileName: string): Promise<{
2864
2964
  writeTime: number;
@@ -2882,6 +2982,7 @@ declare module "sliftutils/storage/remoteStorage/ArchivesUrl" {
2882
2982
  }): Promise<ArchiveFileInfo[]>;
2883
2983
  getURL(path: string): Promise<string>;
2884
2984
  getConfig(): Promise<ArchivesConfig>;
2985
+ hasWriteAccess(): Promise<boolean>;
2885
2986
  }
2886
2987
 
2887
2988
  }
@@ -2894,7 +2995,7 @@ declare module "sliftutils/storage/remoteStorage/accessPage" {
2894
2995
  declare module "sliftutils/storage/remoteStorage/blobStore" {
2895
2996
  /// <reference types="node" />
2896
2997
  /// <reference types="node" />
2897
- import { ArchiveFileInfo, ArchivesSource, ArchivesSyncStatus } from "../IArchives";
2998
+ import { ArchiveFileInfo, ArchivesSource, ArchivesSyncStatus, SyncActivity } from "../IArchives";
2898
2999
  export declare const DEFAULT_FAST_WRITE_DELAY: number;
2899
3000
  export type WriteConfig = {
2900
3001
  fast?: boolean;
@@ -2916,6 +3017,7 @@ declare module "sliftutils/storage/remoteStorage/blobStore" {
2916
3017
  }): Promise<{
2917
3018
  data: Buffer;
2918
3019
  writeTime: number;
3020
+ size: number;
2919
3021
  } | undefined>;
2920
3022
  set(fileName: string, data: Buffer, config?: WriteConfig): Promise<void>;
2921
3023
  del(fileName: string, config?: WriteConfig): Promise<void>;
@@ -2929,6 +3031,17 @@ declare module "sliftutils/storage/remoteStorage/blobStore" {
2929
3031
  }): Promise<ArchiveFileInfo[]>;
2930
3032
  getChangesAfter?(time: number): Promise<ArchiveFileInfo[]>;
2931
3033
  getSyncStatus?(): Promise<ArchivesSyncStatus>;
3034
+ getSyncProgress?(): {
3035
+ index: {
3036
+ fileCount: number;
3037
+ byteCount: number;
3038
+ };
3039
+ syncing: SyncActivity[];
3040
+ };
3041
+ computeIndexTotals?(): Promise<{
3042
+ fileCount: number;
3043
+ byteCount: number;
3044
+ }>;
2932
3045
  startLargeUpload(): Promise<string>;
2933
3046
  appendLargeUpload(id: string, data: Buffer): Promise<void>;
2934
3047
  finishLargeUpload(id: string, key: string): Promise<void>;
@@ -2940,10 +3053,16 @@ declare module "sliftutils/storage/remoteStorage/blobStore" {
2940
3053
  private config?;
2941
3054
  constructor(folder: string, sources: ArchivesSource[], config?: {
2942
3055
  onIndexChanged?: ((key: string) => void) | undefined;
3056
+ readerDiskLimit?: number | undefined;
2943
3057
  } | undefined);
2944
3058
  private stopped;
2945
3059
  private index;
2946
3060
  private mem;
3061
+ private indexFileCount;
3062
+ private indexByteCount;
3063
+ private sourceFileCounts;
3064
+ private sourceByteCounts;
3065
+ private syncActivities;
2947
3066
  private dirty;
2948
3067
  private overlay;
2949
3068
  private sourceStates;
@@ -2954,11 +3073,38 @@ declare module "sliftutils/storage/remoteStorage/blobStore" {
2954
3073
  };
2955
3074
  dispose(): Promise<void>;
2956
3075
  private loadIndex;
3076
+ private countEntry;
2957
3077
  private setIndexEntry;
2958
3078
  private deleteIndexEntry;
3079
+ /** The cheap always-current totals plus any in-progress background synchronization. */
3080
+ getSyncProgress(): {
3081
+ index: {
3082
+ fileCount: number;
3083
+ byteCount: number;
3084
+ };
3085
+ sources: {
3086
+ debugName: string;
3087
+ fileCount: number;
3088
+ byteCount: number;
3089
+ }[];
3090
+ readerDiskLimit?: number;
3091
+ syncing: SyncActivity[];
3092
+ };
3093
+ /** Walks the whole index for exact totals - more expensive than getSyncProgress, but immune to
3094
+ * any drift in the maintained counters (and loads the index first, so it's never cold zeros). */
3095
+ computeIndexTotals(): Promise<{
3096
+ fileCount: number;
3097
+ byteCount: number;
3098
+ sources: {
3099
+ debugName: string;
3100
+ fileCount: number;
3101
+ byteCount: number;
3102
+ }[];
3103
+ }>;
2959
3104
  private flushIndex;
2960
3105
  private runSourceSync;
2961
3106
  private scanSource;
3107
+ private reconcileSource;
2962
3108
  private applyScanned;
2963
3109
  private pollChanges;
2964
3110
  private copySourceFiles;
@@ -2979,12 +3125,13 @@ declare module "sliftutils/storage/remoteStorage/blobStore" {
2979
3125
  }): Promise<{
2980
3126
  data: Buffer;
2981
3127
  writeTime: number;
3128
+ size: number;
2982
3129
  } | undefined>;
2983
3130
  private cacheRead;
2984
3131
  set(key: string, data: Buffer, config?: WriteConfig): Promise<void>;
2985
- private writeToSources;
2986
3132
  del(key: string, config?: WriteConfig): Promise<void>;
2987
- private deleteFromSources;
3133
+ private getWritableSources;
3134
+ private writeToSources;
2988
3135
  getInfo(key: string): Promise<{
2989
3136
  writeTime: number;
2990
3137
  size: number;
@@ -3001,6 +3148,9 @@ declare module "sliftutils/storage/remoteStorage/blobStore" {
3001
3148
  finishLargeUpload(id: string, key: string): Promise<void>;
3002
3149
  cancelLargeUpload(id: string): Promise<void>;
3003
3150
  private flushOverlay;
3151
+ private evicting;
3152
+ private enforceDiskLimit;
3153
+ private cleanupTombstones;
3004
3154
  }
3005
3155
 
3006
3156
  }
@@ -3016,19 +3166,23 @@ declare module "sliftutils/storage/remoteStorage/createArchives" {
3016
3166
  import { IArchives, RemoteConfig, RemoteConfigBase, HostedConfig, BackblazeConfig, ArchiveFileInfo, ArchivesConfig, ArchivesSyncStatus } from "../IArchives";
3017
3167
  export declare function createApiArchives(source: HostedConfig | BackblazeConfig): IArchives;
3018
3168
  export declare class ArchivesChain implements IArchives {
3019
- private normalized;
3020
- private adopted;
3021
- private sourcesPromise;
3169
+ private configured;
3170
+ private activeConfig;
3171
+ private statePromise;
3172
+ private initRetryDelay;
3173
+ private initRetryTimer;
3174
+ private pollTimer;
3175
+ private disposed;
3022
3176
  constructor(config: RemoteConfig | RemoteConfigBase);
3023
3177
  getDebugName(): string;
3024
- private getSourceConfigs;
3025
- private getSources;
3178
+ private getState;
3026
3179
  private init;
3027
- private ensureRouting;
3028
- private readFromSource;
3029
- private read;
3030
- private getAccessHelp;
3031
- private write;
3180
+ private buildSources;
3181
+ private startConfigPoll;
3182
+ private checkForNewConfig;
3183
+ private run;
3184
+ private runWrite;
3185
+ private request;
3032
3186
  waitingForAccess(): Promise<{
3033
3187
  link: string;
3034
3188
  machineId: string;
@@ -3048,11 +3202,14 @@ declare module "sliftutils/storage/remoteStorage/createArchives" {
3048
3202
  }): Promise<{
3049
3203
  data: Buffer;
3050
3204
  writeTime: number;
3205
+ size: number;
3051
3206
  } | undefined>;
3052
3207
  getInfo(fileName: string): Promise<{
3053
3208
  writeTime: number;
3054
3209
  size: number;
3055
3210
  } | undefined>;
3211
+ private selectCoveringSources;
3212
+ private runOnApi;
3056
3213
  find(prefix: string, config?: {
3057
3214
  shallow?: boolean;
3058
3215
  type: "files" | "folders";
@@ -3064,15 +3221,29 @@ declare module "sliftutils/storage/remoteStorage/createArchives" {
3064
3221
  getChangesAfter(time: number): Promise<ArchiveFileInfo[]>;
3065
3222
  getSyncStatus(): Promise<ArchivesSyncStatus>;
3066
3223
  getConfig(): Promise<ArchivesConfig>;
3224
+ /** True only when EVERY write-receiving source would accept our writes (partial write access
3225
+ * desynchronizes sources, so it counts as no access). */
3226
+ hasWriteAccess(): Promise<boolean>;
3227
+ private assertNotBareVariableShard;
3067
3228
  set(fileName: string, data: Buffer, config?: {
3068
3229
  lastModified?: number;
3069
3230
  }): Promise<void>;
3070
3231
  del(fileName: string): Promise<void>;
3232
+ /** Writes a key containing the VARIABLE_SHARD sentinel: picks the lowest-latency up write
3233
+ * shard, materializes the key with a random value inside that shard's route, writes it, and
3234
+ * returns the FULL key actually written (the caller needs it to ever read the value back).
3235
+ * Unlike normal writes this CAN move to another shard when the preferred one is down (error +
3236
+ * socket down, same rule as reads) - each shard receives a different key, so write
3237
+ * consistency is preserved. */
3238
+ setVariableShard(key: string, data: Buffer, config?: {
3239
+ lastModified?: number;
3240
+ }): Promise<string>;
3071
3241
  setLargeFile(config: {
3072
3242
  path: string;
3073
3243
  getNextData(): Promise<Buffer | undefined>;
3074
3244
  }): Promise<void>;
3075
3245
  getURL(path: string): Promise<string>;
3246
+ dispose(): void;
3076
3247
  }
3077
3248
  export declare function createArchives(config: RemoteConfig | RemoteConfigBase): ArchivesChain;
3078
3249
 
@@ -3088,6 +3259,14 @@ declare module "sliftutils/storage/remoteStorage/remoteConfig" {
3088
3259
  /// <reference types="node" />
3089
3260
  import { RemoteConfig, RemoteConfigBase, HostedConfig, BackblazeConfig } from "../IArchives";
3090
3261
  export declare const ROUTING_FILE = "storage/storagerouting.json";
3262
+ /** 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. */
3263
+ export declare function parseVariableRoute(key: string): number | undefined;
3264
+ /** Where a key routes in [0, 1). A materialized variable-shard suffix completely overrides the hash. */
3265
+ export declare function getRoute(key: string): number;
3266
+ export declare function routeContains(route: [number, number] | undefined, value: number): boolean;
3267
+ export declare function routesOverlap(a: [number, number] | undefined, b: [number, number] | undefined): boolean;
3268
+ /** The overlap of two route ranges, or undefined when they don't overlap. */
3269
+ export declare function routeIntersection(a: [number, number] | undefined, b: [number, number] | undefined): [number, number] | undefined;
3091
3270
  export declare function getConfigVersion(config: RemoteConfig): number;
3092
3271
  /** Strips the routing-file suffix, leaving the bucket's public base URL (file paths append to it). */
3093
3272
  export declare function getBucketBaseUrl(url: string): string;
@@ -3108,6 +3287,51 @@ declare module "sliftutils/storage/remoteStorage/remoteConfig" {
3108
3287
 
3109
3288
  }
3110
3289
 
3290
+ declare module "sliftutils/storage/remoteStorage/sourceWrapper" {
3291
+ import { IArchives, HostedConfig, BackblazeConfig } from "../IArchives";
3292
+ import { ArchivesUrl } from "./ArchivesUrl";
3293
+ export declare const RETRY_START_DELAY: number;
3294
+ export declare const RETRY_MAX_DELAY: number;
3295
+ export declare const RETRY_GROWTH = 1.5;
3296
+ export declare class SourceWrapper {
3297
+ config: HostedConfig | BackblazeConfig;
3298
+ private background;
3299
+ api?: IArchives;
3300
+ url?: ArchivesUrl;
3301
+ writeBlocked?: string;
3302
+ private remote?;
3303
+ private disposed;
3304
+ private reconnectRunning;
3305
+ private accessCache?;
3306
+ private constructor();
3307
+ static create(config: HostedConfig | BackblazeConfig, options?: {
3308
+ background?: boolean;
3309
+ }): Promise<SourceWrapper>;
3310
+ getDebugName(): string;
3311
+ isConnected(): boolean;
3312
+ /** Call after a request failed while isConnected() was false: starts (if not already running)
3313
+ * the background reconnect loop. Never blocks - the failed request still throws. */
3314
+ noteFailure(): void;
3315
+ private reconnectLoop;
3316
+ private checkAccess;
3317
+ read<T>(run: (archives: IArchives) => Promise<T>): Promise<T>;
3318
+ hasWriteAccess(): Promise<boolean>;
3319
+ private pings;
3320
+ private pingTimer;
3321
+ /** Starts measuring this source's latency (for variable-shard target preference). Only hosted
3322
+ * remotes are pinged; our own local server counts as 0, everything else as Infinity. */
3323
+ startPinging(): void;
3324
+ /** Median of the recent pings. Sources that can't be pinged sort last (Infinity), except our
3325
+ * own in-process server, which is the best possible target (0). */
3326
+ getLatency(): number;
3327
+ /** Writes always go through the API, so a permission error throws to the caller on every write
3328
+ * (and access granted in the meantime is picked up automatically). */
3329
+ write<T>(run: (archives: IArchives) => Promise<T>): Promise<T>;
3330
+ dispose(): void;
3331
+ }
3332
+
3333
+ }
3334
+
3111
3335
  declare module "sliftutils/storage/remoteStorage/storageController" {
3112
3336
  /// <reference types="node" />
3113
3337
  /// <reference types="node" />
@@ -3142,6 +3366,7 @@ declare module "sliftutils/storage/remoteStorage/storageController" {
3142
3366
  trustedMachines?: TrustRecord[];
3143
3367
  };
3144
3368
  export declare const RemoteStorageController: import("socket-function/SocketFunctionTypes").SocketRegistered<{
3369
+ ping: () => Promise<void>;
3145
3370
  authenticate: (token: AuthToken) => Promise<{
3146
3371
  machineId: string;
3147
3372
  ip: string;
@@ -3167,6 +3392,7 @@ declare module "sliftutils/storage/remoteStorage/storageController" {
3167
3392
  }) => Promise<{
3168
3393
  data: Buffer;
3169
3394
  writeTime: number;
3395
+ size: number;
3170
3396
  } | undefined>;
3171
3397
  set: (account: string, bucketName: string, path: string, data: Buffer, lastModified?: number) => Promise<void>;
3172
3398
  del: (account: string, bucketName: string, path: string) => Promise<void>;
@@ -3180,6 +3406,15 @@ declare module "sliftutils/storage/remoteStorage/storageController" {
3180
3406
  }) => Promise<ArchiveFileInfo[]>;
3181
3407
  getChangesAfter: (account: string, bucketName: string, time: number) => Promise<ArchiveFileInfo[]>;
3182
3408
  getArchivesConfig: (account: string, bucketName: string) => Promise<ArchivesConfig>;
3409
+ getIndexInfo: (account: string, bucketName: string) => Promise<{
3410
+ fileCount: number;
3411
+ byteCount: number;
3412
+ sources: {
3413
+ debugName: string;
3414
+ fileCount: number;
3415
+ byteCount: number;
3416
+ }[];
3417
+ } | undefined>;
3183
3418
  getSyncStatus: (account: string, bucketName: string) => Promise<ArchivesSyncStatus>;
3184
3419
  startLargeFile: (account: string, bucketName: string, path: string) => Promise<string>;
3185
3420
  uploadPart: (uploadId: string, data: Buffer) => Promise<void>;
@@ -3198,11 +3433,6 @@ declare module "sliftutils/storage/remoteStorage/storageServer" {
3198
3433
  export type HostStorageServerConfig = {
3199
3434
  url: string;
3200
3435
  folder: string;
3201
- cloudflareApiToken?: {
3202
- key: string;
3203
- } | {
3204
- path: string;
3205
- };
3206
3436
  lowSpaceThresholdBytes?: number;
3207
3437
  };
3208
3438
  export declare function hostStorageServer(config: HostStorageServerConfig): Promise<void>;
@@ -3237,16 +3467,22 @@ declare module "sliftutils/storage/remoteStorage/storageServerState" {
3237
3467
  export declare function assertWritesAllowed(): void;
3238
3468
  export declare function getTrust(): Promise<IStorage<TrustRecord>>;
3239
3469
  export declare function getRequests(): Promise<IStorage<AccessRequest[]>>;
3470
+ /** Makes machineIds the complete trust list for the account: machines not in the list lose access, machines already trusted keep their existing record, and missing ones are added. */
3471
+ export declare function setTrustedMachines(config: {
3472
+ account: string;
3473
+ machineIds: string[];
3474
+ }): Promise<void>;
3240
3475
  export type LoadedBucket = {
3241
3476
  account: string;
3242
3477
  bucketName: string;
3243
3478
  routing: RemoteConfig;
3244
3479
  routingJSON: string;
3480
+ selfEntries: HostedConfig[];
3245
3481
  self: HostedConfig | undefined;
3246
3482
  store: IBucketStore;
3247
3483
  };
3248
3484
  export declare function getLoadedBucket(account: string, bucketName: string): Promise<LoadedBucket | undefined>;
3249
- export declare function assertMutable(bucket: LoadedBucket, filePath: string): Promise<void>;
3485
+ export declare function assertMutable(bucket: LoadedBucket, filePath: string, writeTime: number): Promise<void>;
3250
3486
  export declare function writeBucketFile(account: string, bucketName: string, filePath: string, data: Buffer, config?: {
3251
3487
  lastModified?: number;
3252
3488
  }): Promise<void>;