sliftutils 1.2.39 → 1.3.1

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.
@@ -1,7 +1,11 @@
1
1
  {
2
2
  "permissions": {
3
3
  "allow": [
4
- "WebFetch(domain:openrouter.ai)"
4
+ "WebFetch(domain:openrouter.ai)",
5
+ "Bash(git pull *)",
6
+ "Bash(echo \"---EXIT:$?---\")",
7
+ "Bash(yarn publish *)",
8
+ "Bash(git push *)"
5
9
  ]
6
10
  }
7
11
  }
package/index.d.ts CHANGED
@@ -55,6 +55,7 @@ declare module "sliftutils/misc/getSecret" {
55
55
  getAllKeys(): string[];
56
56
  get(key: string): Promise<string> | undefined;
57
57
  };
58
+ export declare function setSecret(key: string, value: string): void;
58
59
 
59
60
  }
60
61
 
@@ -714,6 +715,7 @@ declare module "sliftutils/render-utils/colors" {
714
715
  }
715
716
 
716
717
  declare module "sliftutils/render-utils/mobxTyped" {
718
+ export { observable, runInAction, computed, autorun } from "mobx";
717
719
  export declare function configureMobxNextFrameScheduler(): void;
718
720
 
719
721
  }
@@ -760,6 +762,320 @@ declare module "sliftutils/render-utils/observer" {
760
762
 
761
763
  }
762
764
 
765
+ declare module "sliftutils/storage/BulkDatabase2/BulkDatabase2" {
766
+ import { BulkDatabaseBase } from "./BulkDatabaseBase";
767
+ export { BulkDatabaseBase, noopReactiveDeps, bulkDatabase2Timing } from "./BulkDatabaseBase";
768
+ export type { ReactiveDeps, StorageFactory } from "./BulkDatabaseBase";
769
+ /** Per-column on-disk size info, as reported by getColumnInfo/getReaderInfo. */
770
+ export type BulkColumnInfo = {
771
+ column: string;
772
+ byteSize: number;
773
+ };
774
+ /** A snapshot of the collection's shape (no row data), as reported by getReaderInfo. */
775
+ export type BulkReaderInfo = {
776
+ rowCount: number;
777
+ totalBytes: number;
778
+ keyCount: number;
779
+ sampleKey: string | undefined;
780
+ columns: BulkColumnInfo[];
781
+ };
782
+ /**
783
+ * The full public API of BulkDatabase2 (the static `clearCache()` aside). BulkDatabase2 implements
784
+ * this; an application that just wants to depend on the surface can type against this interface instead
785
+ * of reading the implementation. `T` is the row type and must have a string `key`.
786
+ *
787
+ * Reads resolve every key/column by the latest write-time across all storage tiers. Writes are
788
+ * column-merges: a write/update only changes the columns it includes; columns it omits keep their
789
+ * previous value (clear a column by writing it as `undefined`).
790
+ */
791
+ export interface IBulkDatabase2<T extends {
792
+ key: string;
793
+ }> {
794
+ /** The collection name (its folder under the storage root). */
795
+ readonly name: string;
796
+ /** Write one full row (merging its columns onto any existing row for the key). */
797
+ write(entry: T): Promise<void>;
798
+ /** Write many full rows in one batch. */
799
+ writeBatch(entries: T[]): Promise<void>;
800
+ /** Update only the given columns of an existing key (key required). Warns and no-ops if the key isn't present. */
801
+ update(entry: Partial<T> & {
802
+ key: string;
803
+ }): Promise<void>;
804
+ /** Update many keys' partial columns in one batch. */
805
+ updateBatch(entries: (Partial<T> & {
806
+ key: string;
807
+ })[]): Promise<void>;
808
+ /** Delete a key. */
809
+ delete(key: string): Promise<void>;
810
+ /** Delete many keys in one batch. */
811
+ deleteBatch(keys: string[]): Promise<void>;
812
+ /** All live keys. */
813
+ getKeys(): Promise<string[]>;
814
+ /** One field's value for a key, or undefined if the key/column isn't set or the key is deleted. */
815
+ getSingleField<Column extends keyof T>(key: string, column: Column): Promise<T[Column] | undefined>;
816
+ /** A whole column as {key, value} for every live key. */
817
+ getColumn<Column extends keyof T>(column: Column): Promise<{
818
+ key: string;
819
+ value: T[Column];
820
+ }[]>;
821
+ /**
822
+ * Synchronous, reactive read of one field. Returns undefined while the base value is still loading
823
+ * (and re-renders once it arrives, under a mobx observer); reflects pending writes immediately.
824
+ */
825
+ getSingleFieldSync<Column extends keyof T>(key: string, column: Column): T[Column] | undefined;
826
+ /** Synchronous, reactive read of a whole column. undefined while still loading. */
827
+ getColumnSync<Column extends keyof T>(column: Column): {
828
+ key: string;
829
+ value: T[Column];
830
+ }[] | undefined;
831
+ /** The columns present on disk and their byte sizes (no row data read). */
832
+ getColumnInfo(): Promise<BulkColumnInfo[]>;
833
+ /** A cheap snapshot of the collection's shape (row/key counts, total bytes, columns) — no row data. */
834
+ getReaderInfo(): Promise<BulkReaderInfo>;
835
+ /** Consolidate on-disk files. Optional to call; the database also does this in the background. */
836
+ compact(): Promise<void>;
837
+ }
838
+ export declare class BulkDatabase2<T extends {
839
+ key: string;
840
+ }> extends BulkDatabaseBase<T> implements IBulkDatabase2<T> {
841
+ constructor(name: string);
842
+ }
843
+
844
+ }
845
+
846
+ declare module "sliftutils/storage/BulkDatabase2/BulkDatabaseBase" {
847
+ import type { FileStorage } from "../FileFolderAPI";
848
+ export declare const bulkDatabase2Timing: {
849
+ streamSealAgeMs: number;
850
+ foldDataAgeMs: number;
851
+ foldTriggerAgeMs: number;
852
+ foldCheckIntervalMs: number;
853
+ cleanupAgeMs: number;
854
+ cleanupIntervalMs: number;
855
+ };
856
+ export interface ReactiveDeps {
857
+ observe(signal: string): void;
858
+ invalidate(signal: string): void;
859
+ batch(fn: () => void): void;
860
+ }
861
+ export declare const noopReactiveDeps: ReactiveDeps;
862
+ export type StorageFactory = (path: string) => Promise<FileStorage>;
863
+ export declare class BulkDatabaseBase<T extends {
864
+ key: string;
865
+ }> {
866
+ readonly name: string;
867
+ protected deps: ReactiveDeps;
868
+ private storageFactory;
869
+ constructor(name: string, deps: ReactiveDeps, storageFactory: StorageFactory);
870
+ static clearCache(): void;
871
+ storage: {
872
+ (): Promise<FileStorage>;
873
+ reset(): void;
874
+ set(newValue: Promise<FileStorage>): void;
875
+ };
876
+ private overlay;
877
+ private streamTimes;
878
+ private streamFileName;
879
+ private lastCleanup;
880
+ private lastFoldCheck;
881
+ private getStreamFileName;
882
+ private invalidateOverlay;
883
+ private setOverlayRow;
884
+ private setOverlayDeleted;
885
+ private reader;
886
+ private syncSetup;
887
+ private localTime;
888
+ private applyRemote;
889
+ private resetReader;
890
+ write(entry: T): Promise<void>;
891
+ writeBatch(entries: T[]): Promise<void>;
892
+ delete(key: string): Promise<void>;
893
+ deleteBatch(keys: string[]): Promise<void>;
894
+ update(entry: Partial<T> & {
895
+ key: string;
896
+ }): Promise<void>;
897
+ updateBatch(entries: (Partial<T> & {
898
+ key: string;
899
+ })[]): Promise<void>;
900
+ private getValidFiles;
901
+ private commitManifest;
902
+ private writeBulkFile;
903
+ private consolidate;
904
+ private loadStreamEntries;
905
+ private orderStreamEntries;
906
+ private maybeRolloverStream;
907
+ compact(): Promise<void>;
908
+ private makeRawGetRange;
909
+ private loadFileReader;
910
+ private fileLogicalSize;
911
+ private handleUnreadableFile;
912
+ private mergeFilesBase;
913
+ private mergeFiles;
914
+ private mergeFilesLocked;
915
+ private cleanup;
916
+ private formatInfo;
917
+ private patchColumn;
918
+ getSingleField<Column extends keyof T>(key: string, column: Column): Promise<T[Column] | undefined>;
919
+ getColumn<Column extends keyof T>(column: Column): Promise<{
920
+ key: string;
921
+ value: T[Column];
922
+ }[]>;
923
+ getKeys(): Promise<string[]>;
924
+ private baseColumns;
925
+ private baseColumnsLoading;
926
+ private baseFields;
927
+ private baseFieldsLoading;
928
+ private ensureBaseColumn;
929
+ private ensureBaseField;
930
+ getSingleFieldSync<Column extends keyof T>(key: string, column: Column): T[Column] | undefined;
931
+ getColumnSync<Column extends keyof T>(column: Column): {
932
+ key: string;
933
+ value: T[Column];
934
+ }[] | undefined;
935
+ getColumnInfo(): Promise<{
936
+ column: string;
937
+ byteSize: number;
938
+ }[]>;
939
+ getReaderInfo(): Promise<{
940
+ rowCount: number;
941
+ totalBytes: number;
942
+ keyCount: number;
943
+ sampleKey: string | undefined;
944
+ columns: {
945
+ column: string;
946
+ byteSize: number;
947
+ }[];
948
+ }>;
949
+ }
950
+
951
+ }
952
+
953
+ declare module "sliftutils/storage/BulkDatabase2/BulkDatabaseFormat" {
954
+ /// <reference types="node" />
955
+ /// <reference types="node" />
956
+ export declare const KEY_COLUMN = "key";
957
+ export declare const EMPTY_BUFFER: Buffer;
958
+ export declare const ABSENT: unique symbol;
959
+ export declare function buildFileBuffer(rows: Record<string, unknown>[], times: number[]): Buffer[];
960
+ export type BaseBulkDatabaseReader = {
961
+ rowCount: number;
962
+ totalBytes: number;
963
+ minTime: number;
964
+ maxTime: number;
965
+ keys: string[];
966
+ columns: {
967
+ column: string;
968
+ byteSize: number;
969
+ }[];
970
+ keyTimes: Map<string, number>;
971
+ deleteTimes?: Map<string, number>;
972
+ getColumn: (column: string) => Promise<{
973
+ key: string;
974
+ value: unknown;
975
+ time: number;
976
+ }[]>;
977
+ getSingleField: (key: string, column: string) => Promise<{
978
+ value: unknown;
979
+ time: number;
980
+ } | typeof ABSENT>;
981
+ };
982
+ export declare function loadBulkDatabase(config: {
983
+ totalBytes: number;
984
+ getRange: (start: number, end: number) => Promise<Buffer>;
985
+ }): Promise<BaseBulkDatabaseReader>;
986
+
987
+ }
988
+
989
+ declare module "sliftutils/storage/BulkDatabase2/blockCache" {
990
+ /// <reference types="node" />
991
+ /// <reference types="node" />
992
+ export type GetRange = (start: number, end: number) => Promise<Buffer>;
993
+ export declare function encodeCompressedBlocks(data: Buffer): Buffer;
994
+ export declare class BlockCache {
995
+ private blocks;
996
+ private indexes;
997
+ clear(): void;
998
+ private touch;
999
+ private readIndex;
1000
+ open(fileId: string, fileSize: number, rawGetRange: GetRange): Promise<{
1001
+ uncompressedSize: number;
1002
+ getRange: GetRange;
1003
+ }>;
1004
+ private makeGetRange;
1005
+ }
1006
+ export declare const blockCache: BlockCache;
1007
+
1008
+ }
1009
+
1010
+ declare module "sliftutils/storage/BulkDatabase2/manifest" {
1011
+ export declare const MANIFEST_EXTENSION = ".manifest";
1012
+ export type Manifest = {
1013
+ startTime: number;
1014
+ validBulkFiles: string[];
1015
+ ignoredStreamFiles: string[];
1016
+ readFiles: string[];
1017
+ };
1018
+ export declare function isManifestName(name: string): boolean;
1019
+ export declare function manifestFileName(startTime: number, writerId: string, counter: number): string;
1020
+ export declare function parseManifestStartTime(name: string): number | undefined;
1021
+ export declare function chooseManifest(manifests: {
1022
+ name: string;
1023
+ manifest: Manifest;
1024
+ }[]): {
1025
+ name: string;
1026
+ manifest: Manifest;
1027
+ } | undefined;
1028
+
1029
+ }
1030
+
1031
+ declare module "sliftutils/storage/BulkDatabase2/mergeLock" {
1032
+ export declare function tryAcquireMergeLock(collection: string, holderId: string): boolean;
1033
+ export declare function releaseMergeLock(collection: string, holderId: string): void;
1034
+
1035
+ }
1036
+
1037
+ declare module "sliftutils/storage/BulkDatabase2/streamLog" {
1038
+ /// <reference types="node" />
1039
+ /// <reference types="node" />
1040
+ import { BaseBulkDatabaseReader } from "./BulkDatabaseFormat";
1041
+ export declare const STREAM_EXTENSION = ".stream";
1042
+ export type StreamEntry = {
1043
+ time: number;
1044
+ row?: Record<string, unknown>;
1045
+ deletedKey?: string;
1046
+ };
1047
+ export declare function frameRows(entries: {
1048
+ time: number;
1049
+ row: Record<string, unknown>;
1050
+ }[]): Buffer;
1051
+ export declare function frameDeletes(entries: {
1052
+ time: number;
1053
+ key: string;
1054
+ }[]): Buffer;
1055
+ export declare function parseStream(buffer: Buffer): {
1056
+ entries: StreamEntry[];
1057
+ badBytes: number;
1058
+ };
1059
+ export declare function streamReaderFromEntries(entries: StreamEntry[], totalBytes: number): {
1060
+ reader: BaseBulkDatabaseReader;
1061
+ times: Map<string, number>;
1062
+ };
1063
+
1064
+ }
1065
+
1066
+ declare module "sliftutils/storage/BulkDatabase2/syncClient" {
1067
+ export type RemoteWrite = {
1068
+ key: string;
1069
+ time: number;
1070
+ deleted?: boolean;
1071
+ value?: unknown;
1072
+ };
1073
+ export declare function isSyncSupported(): boolean;
1074
+ export declare function connect(collection: string, onWrite: (write: RemoteWrite) => void): Promise<RemoteWrite[]>;
1075
+ export declare function broadcast(collection: string, write: RemoteWrite): void;
1076
+
1077
+ }
1078
+
763
1079
  declare module "sliftutils/storage/CBORStorage" {
764
1080
  /// <reference types="node" />
765
1081
  /// <reference types="node" />
@@ -815,6 +1131,8 @@ declare module "sliftutils/storage/DiskCollection" {
815
1131
  export declare class DiskCollection<T> implements IStorageSync<T> {
816
1132
  private collectionName;
817
1133
  private config?;
1134
+ static getForceNoPrompt(): boolean;
1135
+ static setForceNoPrompt(forceNoPrompt: boolean): void;
818
1136
  constructor(collectionName: string, config?: {
819
1137
  writeDelay?: number | undefined;
820
1138
  cbor?: boolean | undefined;
@@ -984,6 +1302,52 @@ declare module "sliftutils/storage/FileFolderAPI" {
984
1302
  ]>;
985
1303
  };
986
1304
  export declare function setFileAPIKey(key: string): void;
1305
+ export declare class NodeJSFileHandleWrapper implements FileWrapper {
1306
+ private filePath;
1307
+ constructor(filePath: string);
1308
+ getFile(): Promise<{
1309
+ size: number;
1310
+ lastModified: number;
1311
+ arrayBuffer: () => Promise<ArrayBuffer>;
1312
+ slice: (start: number, end: number) => {
1313
+ arrayBuffer: () => Promise<ArrayBuffer>;
1314
+ };
1315
+ }>;
1316
+ createWritable(config?: {
1317
+ keepExistingData?: boolean;
1318
+ }): Promise<{
1319
+ seek: (offset: number) => Promise<void>;
1320
+ write: (value: Buffer) => Promise<void>;
1321
+ close: () => Promise<void>;
1322
+ }>;
1323
+ }
1324
+ export declare class NodeJSDirectoryHandleWrapper implements DirectoryWrapper {
1325
+ private rootPath;
1326
+ constructor(rootPath: string);
1327
+ removeEntry(key: string, options?: {
1328
+ recursive?: boolean;
1329
+ }): Promise<void>;
1330
+ getFileHandle(key: string, options?: {
1331
+ create?: boolean;
1332
+ }): Promise<FileWrapper>;
1333
+ getDirectoryHandle(key: string, options?: {
1334
+ create?: boolean;
1335
+ }): Promise<DirectoryWrapper>;
1336
+ [Symbol.asyncIterator](): AsyncIterableIterator<[
1337
+ string,
1338
+ {
1339
+ kind: "file";
1340
+ name: string;
1341
+ getFile(): Promise<FileWrapper>;
1342
+ } | {
1343
+ kind: "directory";
1344
+ name: string;
1345
+ getDirectoryHandle(key: string, options?: {
1346
+ create?: boolean;
1347
+ }): Promise<DirectoryWrapper>;
1348
+ }
1349
+ ]>;
1350
+ }
987
1351
  export declare const getDirectoryHandle: {
988
1352
  (): Promise<DirectoryWrapper>;
989
1353
  reset(): void;
@@ -997,6 +1361,14 @@ declare module "sliftutils/storage/FileFolderAPI" {
997
1361
  getAllKeys(): string[];
998
1362
  get(key: string): Promise<FileStorage> | undefined;
999
1363
  };
1364
+ export declare const getFileStorageNested2: {
1365
+ (key: string): Promise<FileStorage>;
1366
+ clear(key: string): void;
1367
+ clearAll(): void;
1368
+ forceSet(key: string, value: Promise<FileStorage>): void;
1369
+ getAllKeys(): string[];
1370
+ get(key: string): Promise<FileStorage> | undefined;
1371
+ };
1000
1372
  export declare const getFileStorage: {
1001
1373
  (): Promise<FileStorage>;
1002
1374
  reset(): void;
@@ -1012,6 +1384,8 @@ declare module "sliftutils/storage/FileFolderAPI" {
1012
1384
  export type FileStorage = IStorageRaw & {
1013
1385
  folder: NestedFileStorage;
1014
1386
  };
1387
+ export declare function wrapHandle(handle: DirectoryWrapper): FileStorage;
1388
+ export declare function tryToLoadPointer(pointer: string): Promise<DirectoryWrapper | undefined>;
1015
1389
  export {};
1016
1390
 
1017
1391
  }
@@ -7,3 +7,4 @@ export declare const getSecret: {
7
7
  getAllKeys(): string[];
8
8
  get(key: string): Promise<string> | undefined;
9
9
  };
10
+ export declare function setSecret(key: string, value: string): void;
package/misc/getSecret.ts CHANGED
@@ -96,3 +96,10 @@ export const getSecret = cache(async function getSecret(key: string): Promise<st
96
96
  });
97
97
  });
98
98
  });
99
+
100
+ export function setSecret(key: string, value: string) {
101
+ if (isNode()) {
102
+ throw new Error("setSecret is only supported in the browser. We don't want to break the user's file system with setSecret in NodeJS.");
103
+ }
104
+ localStorage.setItem(getStorageKey(key), value);
105
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sliftutils",
3
- "version": "1.2.39",
3
+ "version": "1.3.1",
4
4
  "main": "index.js",
5
5
  "license": "MIT",
6
6
  "files": [
@@ -51,7 +51,7 @@
51
51
  "mobx": "^6.13.3",
52
52
  "preact-old-types": "^10.28.1",
53
53
  "shell-quote": "^1.8.3",
54
- "socket-function": "^1.1.30",
54
+ "socket-function": "^1.1.42",
55
55
  "typenode": "*",
56
56
  "typesafecss": "*",
57
57
  "ws": "^8.18.3",
@@ -1 +1,2 @@
1
+ export { observable, runInAction, computed, autorun } from "mobx";
1
2
  export declare function configureMobxNextFrameScheduler(): void;
@@ -1,5 +1,9 @@
1
1
  import * as mobx from "mobx";
2
2
  import { batchFunction } from "socket-function/src/batching";
3
+
4
+ // Re-export the core mobx primitives so downstream packages (which may have their own duplicate mobx
5
+ // in node_modules) can share THIS mobx instance, otherwise reactivity doesn't cross package boundaries.
6
+ export { observable, runInAction, computed, autorun } from "mobx";
3
7
  export function configureMobxNextFrameScheduler() {
4
8
  // NOTE: This makes a big difference if we do await calls in a loop which mutates observable state. BUT... we should probably just do those await calls before the loop?
5
9
  let batchReactionScheduler = batchFunction({
@@ -0,0 +1,77 @@
1
+ import { BulkDatabaseBase } from "./BulkDatabaseBase";
2
+ export { BulkDatabaseBase, noopReactiveDeps, bulkDatabase2Timing } from "./BulkDatabaseBase";
3
+ export type { ReactiveDeps, StorageFactory } from "./BulkDatabaseBase";
4
+ /** Per-column on-disk size info, as reported by getColumnInfo/getReaderInfo. */
5
+ export type BulkColumnInfo = {
6
+ column: string;
7
+ byteSize: number;
8
+ };
9
+ /** A snapshot of the collection's shape (no row data), as reported by getReaderInfo. */
10
+ export type BulkReaderInfo = {
11
+ rowCount: number;
12
+ totalBytes: number;
13
+ keyCount: number;
14
+ sampleKey: string | undefined;
15
+ columns: BulkColumnInfo[];
16
+ };
17
+ /**
18
+ * The full public API of BulkDatabase2 (the static `clearCache()` aside). BulkDatabase2 implements
19
+ * this; an application that just wants to depend on the surface can type against this interface instead
20
+ * of reading the implementation. `T` is the row type and must have a string `key`.
21
+ *
22
+ * Reads resolve every key/column by the latest write-time across all storage tiers. Writes are
23
+ * column-merges: a write/update only changes the columns it includes; columns it omits keep their
24
+ * previous value (clear a column by writing it as `undefined`).
25
+ */
26
+ export interface IBulkDatabase2<T extends {
27
+ key: string;
28
+ }> {
29
+ /** The collection name (its folder under the storage root). */
30
+ readonly name: string;
31
+ /** Write one full row (merging its columns onto any existing row for the key). */
32
+ write(entry: T): Promise<void>;
33
+ /** Write many full rows in one batch. */
34
+ writeBatch(entries: T[]): Promise<void>;
35
+ /** Update only the given columns of an existing key (key required). Warns and no-ops if the key isn't present. */
36
+ update(entry: Partial<T> & {
37
+ key: string;
38
+ }): Promise<void>;
39
+ /** Update many keys' partial columns in one batch. */
40
+ updateBatch(entries: (Partial<T> & {
41
+ key: string;
42
+ })[]): Promise<void>;
43
+ /** Delete a key. */
44
+ delete(key: string): Promise<void>;
45
+ /** Delete many keys in one batch. */
46
+ deleteBatch(keys: string[]): Promise<void>;
47
+ /** All live keys. */
48
+ getKeys(): Promise<string[]>;
49
+ /** One field's value for a key, or undefined if the key/column isn't set or the key is deleted. */
50
+ getSingleField<Column extends keyof T>(key: string, column: Column): Promise<T[Column] | undefined>;
51
+ /** A whole column as {key, value} for every live key. */
52
+ getColumn<Column extends keyof T>(column: Column): Promise<{
53
+ key: string;
54
+ value: T[Column];
55
+ }[]>;
56
+ /**
57
+ * Synchronous, reactive read of one field. Returns undefined while the base value is still loading
58
+ * (and re-renders once it arrives, under a mobx observer); reflects pending writes immediately.
59
+ */
60
+ getSingleFieldSync<Column extends keyof T>(key: string, column: Column): T[Column] | undefined;
61
+ /** Synchronous, reactive read of a whole column. undefined while still loading. */
62
+ getColumnSync<Column extends keyof T>(column: Column): {
63
+ key: string;
64
+ value: T[Column];
65
+ }[] | undefined;
66
+ /** The columns present on disk and their byte sizes (no row data read). */
67
+ getColumnInfo(): Promise<BulkColumnInfo[]>;
68
+ /** A cheap snapshot of the collection's shape (row/key counts, total bytes, columns) — no row data. */
69
+ getReaderInfo(): Promise<BulkReaderInfo>;
70
+ /** Consolidate on-disk files. Optional to call; the database also does this in the background. */
71
+ compact(): Promise<void>;
72
+ }
73
+ export declare class BulkDatabase2<T extends {
74
+ key: string;
75
+ }> extends BulkDatabaseBase<T> implements IBulkDatabase2<T> {
76
+ constructor(name: string);
77
+ }
@@ -0,0 +1,103 @@
1
+ import { getFileStorageNested2 } from "../FileFolderAPI";
2
+ import { observable, runInAction } from "../../render-utils/mobxTyped";
3
+ import { BulkDatabaseBase, ReactiveDeps } from "./BulkDatabaseBase";
4
+
5
+ export { BulkDatabaseBase, noopReactiveDeps, bulkDatabase2Timing } from "./BulkDatabaseBase";
6
+ export type { ReactiveDeps, StorageFactory } from "./BulkDatabaseBase";
7
+
8
+ /** Per-column on-disk size info, as reported by getColumnInfo/getReaderInfo. */
9
+ export type BulkColumnInfo = { column: string; byteSize: number };
10
+
11
+ /** A snapshot of the collection's shape (no row data), as reported by getReaderInfo. */
12
+ export type BulkReaderInfo = {
13
+ rowCount: number;
14
+ totalBytes: number;
15
+ keyCount: number;
16
+ sampleKey: string | undefined;
17
+ columns: BulkColumnInfo[];
18
+ };
19
+
20
+ /**
21
+ * The full public API of BulkDatabase2 (the static `clearCache()` aside). BulkDatabase2 implements
22
+ * this; an application that just wants to depend on the surface can type against this interface instead
23
+ * of reading the implementation. `T` is the row type and must have a string `key`.
24
+ *
25
+ * Reads resolve every key/column by the latest write-time across all storage tiers. Writes are
26
+ * column-merges: a write/update only changes the columns it includes; columns it omits keep their
27
+ * previous value (clear a column by writing it as `undefined`).
28
+ */
29
+ export interface IBulkDatabase2<T extends { key: string }> {
30
+ /** The collection name (its folder under the storage root). */
31
+ readonly name: string;
32
+
33
+ /** Write one full row (merging its columns onto any existing row for the key). */
34
+ write(entry: T): Promise<void>;
35
+ /** Write many full rows in one batch. */
36
+ writeBatch(entries: T[]): Promise<void>;
37
+ /** Update only the given columns of an existing key (key required). Warns and no-ops if the key isn't present. */
38
+ update(entry: Partial<T> & { key: string }): Promise<void>;
39
+ /** Update many keys' partial columns in one batch. */
40
+ updateBatch(entries: (Partial<T> & { key: string })[]): Promise<void>;
41
+ /** Delete a key. */
42
+ delete(key: string): Promise<void>;
43
+ /** Delete many keys in one batch. */
44
+ deleteBatch(keys: string[]): Promise<void>;
45
+
46
+ /** All live keys. */
47
+ getKeys(): Promise<string[]>;
48
+ /** One field's value for a key, or undefined if the key/column isn't set or the key is deleted. */
49
+ getSingleField<Column extends keyof T>(key: string, column: Column): Promise<T[Column] | undefined>;
50
+ /** A whole column as {key, value} for every live key. */
51
+ getColumn<Column extends keyof T>(column: Column): Promise<{ key: string; value: T[Column] }[]>;
52
+
53
+ /**
54
+ * Synchronous, reactive read of one field. Returns undefined while the base value is still loading
55
+ * (and re-renders once it arrives, under a mobx observer); reflects pending writes immediately.
56
+ */
57
+ getSingleFieldSync<Column extends keyof T>(key: string, column: Column): T[Column] | undefined;
58
+ /** Synchronous, reactive read of a whole column. undefined while still loading. */
59
+ getColumnSync<Column extends keyof T>(column: Column): { key: string; value: T[Column] }[] | undefined;
60
+
61
+ /** The columns present on disk and their byte sizes (no row data read). */
62
+ getColumnInfo(): Promise<BulkColumnInfo[]>;
63
+ /** A cheap snapshot of the collection's shape (row/key counts, total bytes, columns) — no row data. */
64
+ getReaderInfo(): Promise<BulkReaderInfo>;
65
+
66
+ /** Consolidate on-disk files. Optional to call; the database also does this in the background. */
67
+ compact(): Promise<void>;
68
+ }
69
+
70
+ // mobx-backed reactivity: each signal string gets its own observable box. observe() reads it (so an
71
+ // observer/autorun that calls it tracks that box) and invalidate() bumps it (so those reactions re-run).
72
+ // This reproduces the fine-grained per-key + load-version reactivity the class had when it used
73
+ // observable.map/observable.box directly, while keeping all that logic in the mobx-free base.
74
+ class MobxReactiveDeps implements ReactiveDeps {
75
+ private boxes = new Map<string, { get(): number; set(value: number): void }>();
76
+ private box(signal: string) {
77
+ let box = this.boxes.get(signal);
78
+ if (!box) {
79
+ box = observable.box(0);
80
+ this.boxes.set(signal, box);
81
+ }
82
+ return box;
83
+ }
84
+ observe(signal: string) {
85
+ this.box(signal).get();
86
+ }
87
+ invalidate(signal: string) {
88
+ let box = this.box(signal);
89
+ box.set(box.get() + 1);
90
+ }
91
+ batch(fn: () => void) {
92
+ runInAction(fn);
93
+ }
94
+ }
95
+
96
+ // Backwards-compatible BulkDatabase2: the mobx-reactive flavor. All behavior lives in BulkDatabaseBase;
97
+ // this just supplies mobx reactivity and the default (getFileStorageNested2) storage backend, so the
98
+ // sync reads (getSingleFieldSync / getColumnSync) stay observable for mobx components.
99
+ export class BulkDatabase2<T extends { key: string }> extends BulkDatabaseBase<T> implements IBulkDatabase2<T> {
100
+ constructor(name: string) {
101
+ super(name, new MobxReactiveDeps(), getFileStorageNested2);
102
+ }
103
+ }