sliftutils 1.1.4 → 1.1.40

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 (53) hide show
  1. package/.claude/settings.local.json +7 -0
  2. package/.cursor/rules/api-calls.mdc +16 -0
  3. package/.cursor/rules/coding-styles.mdc +50 -0
  4. package/.cursor/rules/components.mdc +44 -0
  5. package/.cursor/rules/disk-collection.mdc +31 -0
  6. package/.cursor/rules/general-guidelines.mdc +11 -0
  7. package/.cursor/rules/mobx-state.mdc +33 -0
  8. package/.cursor/rules/styling-css.mdc +99 -0
  9. package/CLAUDE.md +211 -0
  10. package/builders/hotReload.ts +23 -20
  11. package/builders/setup.ts +2 -2
  12. package/dist/test.ts.cache +13 -0
  13. package/index.d.ts +166 -14
  14. package/misc/apiKeys.d.ts +1 -0
  15. package/misc/apiKeys.tsx +7 -3
  16. package/misc/dist/apiKeys.tsx.cache +145 -0
  17. package/misc/dist/openrouter.ts.cache +119 -0
  18. package/misc/dist/yaml.ts.cache +45 -0
  19. package/misc/dist/yamlBase.ts.cache +2406 -0
  20. package/misc/https/httpsCerts.d.ts +17 -0
  21. package/misc/https/httpsCerts.ts +4 -4
  22. package/misc/openrouter.d.ts +1 -0
  23. package/misc/openrouter.ts +32 -4
  24. package/misc/zip.d.ts +2 -12
  25. package/misc/zip.ts +2 -97
  26. package/package.json +4 -3
  27. package/render-utils/InputLabel.d.ts +1 -2
  28. package/render-utils/InputLabel.tsx +1 -1
  29. package/render-utils/autoMeasure.d.ts +1 -0
  30. package/render-utils/autoMeasure.ts +43 -0
  31. package/render-utils/dist/Input.tsx.cache +319 -0
  32. package/render-utils/dist/InputLabel.tsx.cache +220 -0
  33. package/render-utils/dist/modal.tsx.cache +69 -0
  34. package/render-utils/dist/observer.tsx.cache +6 -3
  35. package/render-utils/modal.tsx +1 -1
  36. package/storage/DiskCollection.d.ts +17 -0
  37. package/storage/DiskCollection.ts +57 -5
  38. package/storage/FileFolderAPI.d.ts +3 -0
  39. package/storage/FileFolderAPI.tsx +104 -36
  40. package/storage/IStorage.d.ts +4 -0
  41. package/storage/IStorage.ts +3 -0
  42. package/storage/IndexedDBFileFolderAPI.ts +6 -0
  43. package/storage/PrivateFileSystemStorage.d.ts +4 -0
  44. package/storage/PrivateFileSystemStorage.ts +11 -0
  45. package/storage/StorageObservable.d.ts +4 -0
  46. package/storage/StorageObservable.ts +27 -2
  47. package/storage/StorageObservableAsync.d.ts +2 -0
  48. package/storage/StorageObservableAsync.ts +20 -0
  49. package/storage/backblaze.d.ts +97 -0
  50. package/storage/backblaze.ts +915 -0
  51. package/test.ts +10 -0
  52. package/yarn.lock +139 -4
  53. package/.cursorrules +0 -251
@@ -40,6 +40,9 @@ type FileWrapper = {
40
40
  size: number;
41
41
  lastModified: number;
42
42
  arrayBuffer(): Promise<ArrayBuffer>;
43
+ // Matches Blob.slice (which the native File object provides), so the browser
44
+ // implementation works vanilla. End is exclusive, both clamped to the file size.
45
+ slice(start: number, end: number): { arrayBuffer(): Promise<ArrayBuffer> };
43
46
  }>;
44
47
  createWritable(config?: { keepExistingData?: boolean }): Promise<{
45
48
  seek(offset: number): Promise<void>;
@@ -82,7 +85,7 @@ class DirectoryPrompter extends preact.Component {
82
85
  return (
83
86
  <div className={
84
87
  css.position("fixed").pos(0, 0).size("100vw", "100vh")
85
- .zIndex(1)
88
+ .zIndex(2147483647)
86
89
  .background("white")
87
90
  .center
88
91
  .fontSize(40)
@@ -99,13 +102,29 @@ class NodeJSFileHandleWrapper implements FileWrapper {
99
102
 
100
103
  async getFile() {
101
104
  const stats = await fs.promises.stat(this.filePath);
105
+ const filePath = this.filePath;
102
106
  return {
103
107
  size: stats.size,
104
108
  lastModified: stats.mtimeMs,
105
109
  arrayBuffer: async () => {
106
- const buffer = await fs.promises.readFile(this.filePath);
110
+ const buffer = await fs.promises.readFile(filePath);
107
111
  return buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength);
108
- }
112
+ },
113
+ slice: (start: number, end: number) => ({
114
+ arrayBuffer: async () => {
115
+ const clampedStart = Math.min(Math.max(start, 0), stats.size);
116
+ const clampedEnd = Math.min(Math.max(end, clampedStart), stats.size);
117
+ const length = clampedEnd - clampedStart;
118
+ const fileHandle = await fs.promises.open(filePath, "r");
119
+ try {
120
+ const buffer = Buffer.alloc(length);
121
+ await fileHandle.read(buffer, 0, length, clampedStart);
122
+ return buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength);
123
+ } finally {
124
+ await fileHandle.close();
125
+ }
126
+ }
127
+ })
109
128
  };
110
129
  }
111
130
 
@@ -251,30 +270,56 @@ export const getDirectoryHandle = lazy(async function getDirectoryHandle(): Prom
251
270
  doneLoad = true;
252
271
  // Show UI to get user to click and retry
253
272
  let retryCallback: (success: boolean) => void;
254
- let retryPromise = new Promise<boolean>(resolve => {
273
+ let retryReject: (err: Error) => void;
274
+ let retryPromise = new Promise<boolean>((resolve, reject) => {
255
275
  retryCallback = resolve;
276
+ retryReject = reject;
256
277
  });
257
278
  displayData.ui = (
258
- <button
259
- className={css.fontSize(40).pad2(80, 40)}
260
- onClick={async () => {
261
- displayData.ui = "Loading...";
262
- try {
263
- const retryHandle = await tryToLoadPointer(storedId);
264
- if (retryHandle) {
265
- handle = retryHandle;
266
- retryCallback(true);
267
- } else {
279
+ <div className={css.vbox(20).center}>
280
+ <button
281
+ className={css.fontSize(40).pad2(80, 40)}
282
+ onClick={async () => {
283
+ displayData.ui = "Loading...";
284
+ try {
285
+ const retryHandle = await tryToLoadPointer(storedId);
286
+ if (retryHandle) {
287
+ handle = retryHandle;
288
+ retryCallback(true);
289
+ } else {
290
+ retryCallback(false);
291
+ }
292
+ } catch (retryError) {
293
+ console.error("Retry failed:", retryError);
268
294
  retryCallback(false);
269
295
  }
270
- } catch (retryError) {
271
- console.error("Retry failed:", retryError);
272
- retryCallback(false);
273
- }
274
- }}
275
- >
276
- Click to restore file system access
277
- </button>
296
+ }}
297
+ >
298
+ Click to restore file system access
299
+ </button>
300
+ <button
301
+ className={css.fontSize(40).pad2(80, 40)}
302
+ onClick={async () => {
303
+ console.log("Waiting for user to give permission");
304
+ const pickedHandle = await window.showDirectoryPicker();
305
+ await pickedHandle.requestPermission({ mode: "readwrite" });
306
+ let newStoredId = await storeFileSystemPointer({ mode: "readwrite", handle: pickedHandle });
307
+ localStorage.setItem(getFileAPIKey(), newStoredId);
308
+ handle = pickedHandle as any;
309
+ retryCallback(true);
310
+ }}
311
+ >
312
+ Pick Data Directory
313
+ </button>
314
+ <button
315
+ className={css.fontSize(40).pad2(80, 40)}
316
+ onClick={() => {
317
+ retryReject(new Error("User dismissed file system access prompt"));
318
+ }}
319
+ >
320
+ Dismiss
321
+ </button>
322
+ </div>
278
323
  );
279
324
  const success = await retryPromise;
280
325
  if (handle) {
@@ -288,23 +333,35 @@ export const getDirectoryHandle = lazy(async function getDirectoryHandle(): Prom
288
333
  }
289
334
  }
290
335
  let fileCallback: (handle: DirectoryWrapper) => void;
291
- let promise = new Promise<DirectoryWrapper>(resolve => {
336
+ let fileReject: (err: Error) => void;
337
+ let promise = new Promise<DirectoryWrapper>((resolve, reject) => {
292
338
  fileCallback = resolve;
339
+ fileReject = reject;
293
340
  });
294
341
  displayData.ui = (
295
- <button
296
- className={css.fontSize(40).pad2(80, 40)}
297
- onClick={async () => {
298
- console.log("Waiting for user to give permission");
299
- const handle = await window.showDirectoryPicker();
300
- await handle.requestPermission({ mode: "readwrite" });
301
- let storedId = await storeFileSystemPointer({ mode: "readwrite", handle });
302
- localStorage.setItem(getFileAPIKey(), storedId);
303
- fileCallback(handle as any);
304
- }}
305
- >
306
- Pick Data Directory
307
- </button>
342
+ <div className={css.vbox(20).center}>
343
+ <button
344
+ className={css.fontSize(40).pad2(80, 40)}
345
+ onClick={async () => {
346
+ console.log("Waiting for user to give permission");
347
+ const handle = await window.showDirectoryPicker();
348
+ await handle.requestPermission({ mode: "readwrite" });
349
+ let storedId = await storeFileSystemPointer({ mode: "readwrite", handle });
350
+ localStorage.setItem(getFileAPIKey(), storedId);
351
+ fileCallback(handle as any);
352
+ }}
353
+ >
354
+ Pick Data Directory
355
+ </button>
356
+ <button
357
+ className={css.fontSize(40).pad2(80, 40)}
358
+ onClick={() => {
359
+ fileReject(new Error("User dismissed file system access prompt"));
360
+ }}
361
+ >
362
+ Dismiss
363
+ </button>
364
+ </div>
308
365
  );
309
366
  return await promise;
310
367
  } finally {
@@ -402,6 +459,17 @@ function wrapHandleFiles(handle: DirectoryWrapper): IStorageRaw {
402
459
  }
403
460
  },
404
461
 
462
+ async getRange(key: string, config: { start: number; end: number }): Promise<Buffer | undefined> {
463
+ try {
464
+ const file = await handle.getFileHandle(key);
465
+ const fileContent = await file.getFile();
466
+ const arrayBuffer = await fileContent.slice(config.start, config.end).arrayBuffer();
467
+ return Buffer.from(arrayBuffer);
468
+ } catch (error) {
469
+ return undefined;
470
+ }
471
+ },
472
+
405
473
  async append(key: string, value: Buffer): Promise<void> {
406
474
  await appendQueue(key)(async () => {
407
475
  // NOTE: Interesting point. Chrome doesn't optimize this to be an append, and instead
@@ -27,6 +27,10 @@ export type IStorage<T> = {
27
27
  };
28
28
  export type IStorageRaw = {
29
29
  get(key: string): Promise<Buffer | undefined>;
30
+ getRange(key: string, config: {
31
+ start: number;
32
+ end: number;
33
+ }): Promise<Buffer | undefined>;
30
34
  append(key: string, value: Buffer): Promise<void>;
31
35
  set(key: string, value: Buffer): Promise<void>;
32
36
  remove(key: string): Promise<void>;
@@ -29,6 +29,9 @@ export type IStorage<T> = {
29
29
  // (/ makes a folder). And there are even more rules, such as lengths per folder, etc, etc.
30
30
  export type IStorageRaw = {
31
31
  get(key: string): Promise<Buffer | undefined>;
32
+ // Reads bytes in the range [start, end) (end is exclusive, clamped to the file size).
33
+ // Returns undefined if the file doesn't exist.
34
+ getRange(key: string, config: { start: number; end: number }): Promise<Buffer | undefined>;
32
35
  // May or may not be efficient in the underlying storage
33
36
  append(key: string, value: Buffer): Promise<void>;
34
37
  set(key: string, value: Buffer): Promise<void>;
@@ -44,6 +44,12 @@ class VirtualFileStorage implements FileStorage {
44
44
  return badBuffer;
45
45
  }
46
46
 
47
+ async getRange(key: string, config: { start: number; end: number }): Promise<Buffer | undefined> {
48
+ const fullBuffer = await this.get(key);
49
+ if (!fullBuffer) return undefined;
50
+ return fullBuffer.subarray(config.start, config.end);
51
+ }
52
+
47
53
  async append(key: string, value: Buffer): Promise<void> {
48
54
  const store = this.getStore("readwrite");
49
55
  const fullPath = this.id + key;
@@ -11,6 +11,10 @@ export declare class PrivateFileSystemStorage implements IStorageRaw {
11
11
  private getFileHandle;
12
12
  private fileExists;
13
13
  get(key: string): Promise<Buffer | undefined>;
14
+ getRange(key: string, config: {
15
+ start: number;
16
+ end: number;
17
+ }): Promise<Buffer | undefined>;
14
18
  set(key: string, value: Buffer): Promise<void>;
15
19
  append(key: string, value: Buffer): Promise<void>;
16
20
  remove(key: string): Promise<void>;
@@ -114,6 +114,17 @@ export class PrivateFileSystemStorage implements IStorageRaw {
114
114
  }
115
115
  }
116
116
 
117
+ public async getRange(key: string, config: { start: number; end: number }): Promise<Buffer | undefined> {
118
+ const fileHandle = await this.getFileHandle(key, false);
119
+ if (!fileHandle) {
120
+ return undefined;
121
+ }
122
+
123
+ const file = await fileHandle.getFile();
124
+ const arrayBuffer = await file.slice(config.start, config.end).arrayBuffer();
125
+ return Buffer.from(arrayBuffer);
126
+ }
127
+
117
128
  public async set(key: string, value: Buffer): Promise<void> {
118
129
  try {
119
130
  const fileHandle = await this.getFileHandle(key, true);
@@ -1,4 +1,7 @@
1
1
  import { IStorage, IStorageSync } from "./IStorage";
2
+ export declare const storagePendingAccesses: {
3
+ value: number;
4
+ };
2
5
  export declare class StorageSync<T> implements IStorageSync<T> {
3
6
  storage: IStorage<T>;
4
7
  private config?;
@@ -38,3 +41,4 @@ export declare class StorageSync<T> implements IStorageSync<T> {
38
41
  reloadKey(key: string): void;
39
42
  reset(): Promise<void>;
40
43
  }
44
+ export declare function waitUntilNextLoad(): Promise<void>;
@@ -1,8 +1,11 @@
1
1
  import { observable } from "mobx";
2
2
  import { deepFreezeObject, freezeObject, isDefined } from "../misc/types";
3
3
  import { IStorage, IStorageSync } from "./IStorage";
4
+ import { PromiseObj } from "socket-function/src/misc";
4
5
 
5
- // NOTE: At around 500K values (depending on their size to some degree), this will take about 2 minutes to load. But once it does it will be fast. So... keep that in mind. I recommend not exceeding 100K
6
+ export const storagePendingAccesses = { value: 0 };
7
+
8
+ // NOTE: At around 500K values (depending on their size to some degree), this will take about 2 minutes to load. But once it does it will be fast. So... keep that in mind. I recommend not exceeding 100K.
6
9
  export class StorageSync<T> implements IStorageSync<T> {
7
10
  cached = observable.map<string, T | undefined>(undefined, { deep: false });
8
11
  infoCached = observable.map<string, { size: number; lastModified: number } | undefined>(undefined, { deep: false });
@@ -85,6 +88,7 @@ export class StorageSync<T> implements IStorageSync<T> {
85
88
  public async getPromise(key: string): Promise<T | undefined> {
86
89
  let value = this.cached.get(key);
87
90
  if (value === undefined) {
91
+ storagePendingAccesses.value++;
88
92
  value = await this.storage.get(key);
89
93
  if (value !== undefined && this.cached.get(key) === undefined) {
90
94
  if (this.config?.freeze === "shallow") {
@@ -94,15 +98,19 @@ export class StorageSync<T> implements IStorageSync<T> {
94
98
  }
95
99
  this.cached.set(key, value);
96
100
  }
101
+ triggerLoad();
97
102
  }
98
103
  return value;
99
104
  }
100
105
  private pendingGetKeys: Promise<string[]> | undefined;
101
106
  public async getKeysPromise(): Promise<string[]> {
107
+ if (this.loadedKeys) {
108
+ return Array.from(this.keys);
109
+ }
110
+ storagePendingAccesses.value++;
102
111
  if (this.pendingGetKeys) {
103
112
  return this.pendingGetKeys;
104
113
  }
105
- if (this.loadedKeys) return Array.from(this.keys);
106
114
  this.loadedKeys = true;
107
115
  this.pendingGetKeys = this.storage.getKeys();
108
116
  void this.pendingGetKeys.finally(() => {
@@ -113,6 +121,7 @@ export class StorageSync<T> implements IStorageSync<T> {
113
121
  this.keys = new Set(keys);
114
122
  this.synced.keySeqNum++;
115
123
  }
124
+ triggerLoad();
116
125
  return Array.from(this.keys);
117
126
  }
118
127
 
@@ -140,4 +149,20 @@ export class StorageSync<T> implements IStorageSync<T> {
140
149
  this.synced.keySeqNum++;
141
150
  await this.storage.reset();
142
151
  }
152
+ }
153
+
154
+ let waitUntilNextLoadWatchers: PromiseObj<void>[] = [];
155
+ export function waitUntilNextLoad(): Promise<void> {
156
+ let promise = new PromiseObj<void>();
157
+ waitUntilNextLoadWatchers.push(promise);
158
+ return promise.promise;
159
+ }
160
+ function triggerLoad() {
161
+ let watchers = waitUntilNextLoadWatchers;
162
+ waitUntilNextLoadWatchers = [];
163
+ void Promise.resolve().finally(() => {
164
+ for (let watcher of watchers) {
165
+ watcher.resolve();
166
+ }
167
+ });
143
168
  }
@@ -0,0 +1,2 @@
1
+ /** Reruns the code until all StorageSyncs accessed have loaded their values. Not efficient,although will usually be O(values accessed), just due to how loading works (it won't be quadratic). */
2
+ export declare function rerunCodeUntilAllLoaded<T>(code: () => T): Promise<T>;
@@ -0,0 +1,20 @@
1
+ import { storagePendingAccesses, waitUntilNextLoad } from "./StorageObservable";
2
+
3
+ /** Reruns the code until all StorageSyncs accessed have loaded their values. Not efficient,although will usually be O(values accessed), just due to how loading works (it won't be quadratic). */
4
+ export async function rerunCodeUntilAllLoaded<T>(code: () => T): Promise<T> {
5
+ while (true) {
6
+ let beforeAccesses = storagePendingAccesses.value;
7
+ try {
8
+ let result = await code();
9
+ if (storagePendingAccesses.value === beforeAccesses) {
10
+ return result;
11
+ }
12
+ } catch (error) {
13
+ if (storagePendingAccesses.value === beforeAccesses) {
14
+ throw error;
15
+ }
16
+ }
17
+ console.log(`Rerunning synchronous check as loading starting while evaluating code. Function name`, code);
18
+ await waitUntilNextLoad();
19
+ }
20
+ }
@@ -0,0 +1,97 @@
1
+ /// <reference types="node" />
2
+ /// <reference types="node" />
3
+ export declare class ArchivesBackblaze {
4
+ private config;
5
+ constructor(config: {
6
+ bucketName: string;
7
+ public?: boolean;
8
+ immutable?: boolean;
9
+ cacheTime?: number;
10
+ });
11
+ private bucketName;
12
+ private bucketId;
13
+ private logging;
14
+ enableLogging(): void;
15
+ private log;
16
+ getDebugName(): string;
17
+ private getBucketAPI;
18
+ private last503Reset;
19
+ private apiRetryLogic;
20
+ get(fileName: string, config?: {
21
+ range?: {
22
+ start: number;
23
+ end: number;
24
+ };
25
+ retryCount?: number;
26
+ }): Promise<Buffer | undefined>;
27
+ set(fileName: string, data: Buffer): Promise<void>;
28
+ del(fileName: string): Promise<void>;
29
+ setLargeFile(config: {
30
+ path: string;
31
+ getNextData(): Promise<Buffer | undefined>;
32
+ }): Promise<void>;
33
+ getInfo(fileName: string): Promise<{
34
+ writeTime: number;
35
+ size: number;
36
+ } | undefined>;
37
+ find(prefix: string, config?: {
38
+ shallow?: boolean;
39
+ type: "files" | "folders";
40
+ }): Promise<string[]>;
41
+ findInfo(prefix: string, config?: {
42
+ shallow?: boolean;
43
+ type: "files" | "folders";
44
+ }): Promise<{
45
+ path: string;
46
+ createTime: number;
47
+ size: number;
48
+ }[]>;
49
+ assertPathValid(path: string): Promise<void>;
50
+ getURL(path: string): Promise<string>;
51
+ getDownloadAuthorization(config: {
52
+ fileNamePrefix?: string;
53
+ validDurationInSeconds: number;
54
+ b2ContentDisposition?: string;
55
+ b2ContentLanguage?: string;
56
+ b2Expires?: string;
57
+ b2CacheControl?: string;
58
+ b2ContentEncoding?: string;
59
+ b2ContentType?: string;
60
+ }): Promise<{
61
+ bucketId: string;
62
+ fileNamePrefix: string;
63
+ authorizationToken: string;
64
+ }>;
65
+ }
66
+ export declare const getArchivesBackblaze: {
67
+ (key: string): ArchivesBackblaze;
68
+ clear(key: string): void;
69
+ clearAll(): void;
70
+ forceSet(key: string, value: ArchivesBackblaze): void;
71
+ getAllKeys(): string[];
72
+ get(key: string): ArchivesBackblaze | undefined;
73
+ };
74
+ export declare const getArchivesBackblazePrivateImmutable: {
75
+ (key: string): ArchivesBackblaze;
76
+ clear(key: string): void;
77
+ clearAll(): void;
78
+ forceSet(key: string, value: ArchivesBackblaze): void;
79
+ getAllKeys(): string[];
80
+ get(key: string): ArchivesBackblaze | undefined;
81
+ };
82
+ export declare const getArchivesBackblazePublicImmutable: {
83
+ (key: string): ArchivesBackblaze;
84
+ clear(key: string): void;
85
+ clearAll(): void;
86
+ forceSet(key: string, value: ArchivesBackblaze): void;
87
+ getAllKeys(): string[];
88
+ get(key: string): ArchivesBackblaze | undefined;
89
+ };
90
+ export declare const getArchivesBackblazePublic: {
91
+ (key: string): ArchivesBackblaze;
92
+ clear(key: string): void;
93
+ clearAll(): void;
94
+ forceSet(key: string, value: ArchivesBackblaze): void;
95
+ getAllKeys(): string[];
96
+ get(key: string): ArchivesBackblaze | undefined;
97
+ };