sliftutils 1.3.0 → 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.
package/index.d.ts CHANGED
@@ -764,11 +764,80 @@ declare module "sliftutils/render-utils/observer" {
764
764
 
765
765
  declare module "sliftutils/storage/BulkDatabase2/BulkDatabase2" {
766
766
  import { BulkDatabaseBase } from "./BulkDatabaseBase";
767
- export { BulkDatabaseBase, noopReactiveDeps } from "./BulkDatabaseBase";
767
+ export { BulkDatabaseBase, noopReactiveDeps, bulkDatabase2Timing } from "./BulkDatabaseBase";
768
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
+ }
769
838
  export declare class BulkDatabase2<T extends {
770
839
  key: string;
771
- }> extends BulkDatabaseBase<T> {
840
+ }> extends BulkDatabaseBase<T> implements IBulkDatabase2<T> {
772
841
  constructor(name: string);
773
842
  }
774
843
 
@@ -776,6 +845,14 @@ declare module "sliftutils/storage/BulkDatabase2/BulkDatabase2" {
776
845
 
777
846
  declare module "sliftutils/storage/BulkDatabase2/BulkDatabaseBase" {
778
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
+ };
779
856
  export interface ReactiveDeps {
780
857
  observe(signal: string): void;
781
858
  invalidate(signal: string): void;
@@ -799,9 +876,12 @@ declare module "sliftutils/storage/BulkDatabase2/BulkDatabaseBase" {
799
876
  private overlay;
800
877
  private streamTimes;
801
878
  private streamFileName;
802
- private streamRowsWritten;
879
+ private lastCleanup;
880
+ private lastFoldCheck;
803
881
  private getStreamFileName;
804
- private setOverlay;
882
+ private invalidateOverlay;
883
+ private setOverlayRow;
884
+ private setOverlayDeleted;
805
885
  private reader;
806
886
  private syncSetup;
807
887
  private localTime;
@@ -811,20 +891,28 @@ declare module "sliftutils/storage/BulkDatabase2/BulkDatabaseBase" {
811
891
  writeBatch(entries: T[]): Promise<void>;
812
892
  delete(key: string): Promise<void>;
813
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;
814
902
  private writeBulkFile;
815
- private listStreamFiles;
903
+ private consolidate;
816
904
  private loadStreamEntries;
817
905
  private orderStreamEntries;
818
906
  private maybeRolloverStream;
819
- private rolloverStream;
820
907
  compact(): Promise<void>;
821
- private listFiles;
822
908
  private makeRawGetRange;
823
909
  private loadFileReader;
824
910
  private fileLogicalSize;
825
911
  private handleUnreadableFile;
826
912
  private mergeFilesBase;
827
913
  private mergeFiles;
914
+ private mergeFilesLocked;
915
+ private cleanup;
828
916
  private formatInfo;
829
917
  private patchColumn;
830
918
  getSingleField<Column extends keyof T>(key: string, column: Column): Promise<T[Column] | undefined>;
@@ -867,21 +955,29 @@ declare module "sliftutils/storage/BulkDatabase2/BulkDatabaseFormat" {
867
955
  /// <reference types="node" />
868
956
  export declare const KEY_COLUMN = "key";
869
957
  export declare const EMPTY_BUFFER: Buffer;
870
- export declare function buildFileBuffer(rows: Record<string, unknown>[]): Buffer[];
958
+ export declare const ABSENT: unique symbol;
959
+ export declare function buildFileBuffer(rows: Record<string, unknown>[], times: number[]): Buffer[];
871
960
  export type BaseBulkDatabaseReader = {
872
961
  rowCount: number;
873
962
  totalBytes: number;
963
+ minTime: number;
964
+ maxTime: number;
874
965
  keys: string[];
875
966
  columns: {
876
967
  column: string;
877
968
  byteSize: number;
878
969
  }[];
879
- deletedKeys?: Set<string>;
970
+ keyTimes: Map<string, number>;
971
+ deleteTimes?: Map<string, number>;
880
972
  getColumn: (column: string) => Promise<{
881
973
  key: string;
882
974
  value: unknown;
975
+ time: number;
883
976
  }[]>;
884
- getSingleField: (key: string, column: string) => Promise<unknown | undefined>;
977
+ getSingleField: (key: string, column: string) => Promise<{
978
+ value: unknown;
979
+ time: number;
980
+ } | typeof ABSENT>;
885
981
  };
886
982
  export declare function loadBulkDatabase(config: {
887
983
  totalBytes: number;
@@ -911,6 +1007,33 @@ declare module "sliftutils/storage/BulkDatabase2/blockCache" {
911
1007
 
912
1008
  }
913
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
+
914
1037
  declare module "sliftutils/storage/BulkDatabase2/streamLog" {
915
1038
  /// <reference types="node" />
916
1039
  /// <reference types="node" />
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sliftutils",
3
- "version": "1.3.0",
3
+ "version": "1.3.1",
4
4
  "main": "index.js",
5
5
  "license": "MIT",
6
6
  "files": [
@@ -1,8 +1,77 @@
1
1
  import { BulkDatabaseBase } from "./BulkDatabaseBase";
2
- export { BulkDatabaseBase, noopReactiveDeps } from "./BulkDatabaseBase";
2
+ export { BulkDatabaseBase, noopReactiveDeps, bulkDatabase2Timing } from "./BulkDatabaseBase";
3
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
+ }
4
73
  export declare class BulkDatabase2<T extends {
5
74
  key: string;
6
- }> extends BulkDatabaseBase<T> {
75
+ }> extends BulkDatabaseBase<T> implements IBulkDatabase2<T> {
7
76
  constructor(name: string);
8
77
  }
@@ -2,9 +2,71 @@ import { getFileStorageNested2 } from "../FileFolderAPI";
2
2
  import { observable, runInAction } from "../../render-utils/mobxTyped";
3
3
  import { BulkDatabaseBase, ReactiveDeps } from "./BulkDatabaseBase";
4
4
 
5
- export { BulkDatabaseBase, noopReactiveDeps } from "./BulkDatabaseBase";
5
+ export { BulkDatabaseBase, noopReactiveDeps, bulkDatabase2Timing } from "./BulkDatabaseBase";
6
6
  export type { ReactiveDeps, StorageFactory } from "./BulkDatabaseBase";
7
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
+
8
70
  // mobx-backed reactivity: each signal string gets its own observable box. observe() reads it (so an
9
71
  // observer/autorun that calls it tracks that box) and invalidate() bumps it (so those reactions re-run).
10
72
  // This reproduces the fine-grained per-key + load-version reactivity the class had when it used
@@ -34,7 +96,7 @@ class MobxReactiveDeps implements ReactiveDeps {
34
96
  // Backwards-compatible BulkDatabase2: the mobx-reactive flavor. All behavior lives in BulkDatabaseBase;
35
97
  // this just supplies mobx reactivity and the default (getFileStorageNested2) storage backend, so the
36
98
  // sync reads (getSingleFieldSync / getColumnSync) stay observable for mobx components.
37
- export class BulkDatabase2<T extends { key: string }> extends BulkDatabaseBase<T> {
99
+ export class BulkDatabase2<T extends { key: string }> extends BulkDatabaseBase<T> implements IBulkDatabase2<T> {
38
100
  constructor(name: string) {
39
101
  super(name, new MobxReactiveDeps(), getFileStorageNested2);
40
102
  }
@@ -1,4 +1,12 @@
1
1
  import type { FileStorage } from "../FileFolderAPI";
2
+ export declare const bulkDatabase2Timing: {
3
+ streamSealAgeMs: number;
4
+ foldDataAgeMs: number;
5
+ foldTriggerAgeMs: number;
6
+ foldCheckIntervalMs: number;
7
+ cleanupAgeMs: number;
8
+ cleanupIntervalMs: number;
9
+ };
2
10
  export interface ReactiveDeps {
3
11
  observe(signal: string): void;
4
12
  invalidate(signal: string): void;
@@ -22,9 +30,12 @@ export declare class BulkDatabaseBase<T extends {
22
30
  private overlay;
23
31
  private streamTimes;
24
32
  private streamFileName;
25
- private streamRowsWritten;
33
+ private lastCleanup;
34
+ private lastFoldCheck;
26
35
  private getStreamFileName;
27
- private setOverlay;
36
+ private invalidateOverlay;
37
+ private setOverlayRow;
38
+ private setOverlayDeleted;
28
39
  private reader;
29
40
  private syncSetup;
30
41
  private localTime;
@@ -34,20 +45,28 @@ export declare class BulkDatabaseBase<T extends {
34
45
  writeBatch(entries: T[]): Promise<void>;
35
46
  delete(key: string): Promise<void>;
36
47
  deleteBatch(keys: string[]): Promise<void>;
48
+ update(entry: Partial<T> & {
49
+ key: string;
50
+ }): Promise<void>;
51
+ updateBatch(entries: (Partial<T> & {
52
+ key: string;
53
+ })[]): Promise<void>;
54
+ private getValidFiles;
55
+ private commitManifest;
37
56
  private writeBulkFile;
38
- private listStreamFiles;
57
+ private consolidate;
39
58
  private loadStreamEntries;
40
59
  private orderStreamEntries;
41
60
  private maybeRolloverStream;
42
- private rolloverStream;
43
61
  compact(): Promise<void>;
44
- private listFiles;
45
62
  private makeRawGetRange;
46
63
  private loadFileReader;
47
64
  private fileLogicalSize;
48
65
  private handleUnreadableFile;
49
66
  private mergeFilesBase;
50
67
  private mergeFiles;
68
+ private mergeFilesLocked;
69
+ private cleanup;
51
70
  private formatInfo;
52
71
  private patchColumn;
53
72
  getSingleField<Column extends keyof T>(key: string, column: Column): Promise<T[Column] | undefined>;